init
This commit is contained in:
commit
eec520f969
21
composer.json
Normal file
21
composer.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "laysense/monitor",
|
||||||
|
"description": "A package to use Websocket API of ping0.cc to monitor the ping latency of target host [TEST ONLY]",
|
||||||
|
"type": "library",
|
||||||
|
"require": {
|
||||||
|
"textalk/websocket": "dev-master"
|
||||||
|
},
|
||||||
|
"license": "AGPL",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Laysense\\Monitor\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Enoch",
|
||||||
|
"email": "enoch@laysese.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minimum-stability": "dev"
|
||||||
|
}
|
375
composer.lock
generated
Normal file
375
composer.lock
generated
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "7f9863daaab5193cdcf774ddaa946c8d",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "phrity/net-uri",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sirn-se/phrity-net-uri.git",
|
||||||
|
"reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/3f458e0c4d1ddc0e218d7a5b9420127c63925f43",
|
||||||
|
"reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0",
|
||||||
|
"psr/http-factory": "^1.0",
|
||||||
|
"psr/http-message": "^1.0 | ^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.0 | ^10.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Net\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen",
|
||||||
|
"email": "sirn@sirn.se",
|
||||||
|
"homepage": "https://phrity.sirn.se"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PSR-7 Uri and PSR-17 UriFactory implementation",
|
||||||
|
"homepage": "https://phrity.sirn.se/net-uri",
|
||||||
|
"keywords": [
|
||||||
|
"psr-17",
|
||||||
|
"psr-7",
|
||||||
|
"uri",
|
||||||
|
"uri factory"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/sirn-se/phrity-net-uri/issues",
|
||||||
|
"source": "https://github.com/sirn-se/phrity-net-uri/tree/1.3.0"
|
||||||
|
},
|
||||||
|
"time": "2023-08-21T10:33:06+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phrity/util-errorhandler",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sirn-se/phrity-util-errorhandler.git",
|
||||||
|
"reference": "483228156e06673963902b1cc1e6bd9541ab4d5e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/483228156e06673963902b1cc1e6bd9541ab4d5e",
|
||||||
|
"reference": "483228156e06673963902b1cc1e6bd9541ab4d5e",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.0 | ^10.0 | ^11.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Util\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen",
|
||||||
|
"email": "sirn@sirn.se",
|
||||||
|
"homepage": "https://phrity.sirn.se"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Inline error handler; catch and resolve errors for code block.",
|
||||||
|
"homepage": "https://phrity.sirn.se/util-errorhandler",
|
||||||
|
"keywords": [
|
||||||
|
"error",
|
||||||
|
"warning"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues",
|
||||||
|
"source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.1.1"
|
||||||
|
},
|
||||||
|
"time": "2024-09-12T06:49:16+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-factory",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-factory.git",
|
||||||
|
"reference": "7037f4b0950474e9d1350e8df89b15f1842085f6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/7037f4b0950474e9d1350e8df89b15f1842085f6",
|
||||||
|
"reference": "7037f4b0950474e9d1350e8df89b15f1842085f6",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.0.0",
|
||||||
|
"psr/http-message": "^1.0 || ^2.0"
|
||||||
|
},
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||||
|
"keywords": [
|
||||||
|
"factory",
|
||||||
|
"http",
|
||||||
|
"message",
|
||||||
|
"psr",
|
||||||
|
"psr-17",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-factory"
|
||||||
|
},
|
||||||
|
"time": "2023-09-22T11:16:44+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-message",
|
||||||
|
"version": "1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-message.git",
|
||||||
|
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||||
|
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.1.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for HTTP messages",
|
||||||
|
"homepage": "https://github.com/php-fig/http-message",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"http-message",
|
||||||
|
"psr",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
||||||
|
},
|
||||||
|
"time": "2023-04-04T09:50:52+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/log.git",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"psr",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/log/tree/3.0.2"
|
||||||
|
},
|
||||||
|
"time": "2024-09-11T13:17:53+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "textalk/websocket",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Textalk/websocket-php.git",
|
||||||
|
"reference": "34b2f0efa2e1c071b046e2b98848178fddf21552"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/34b2f0efa2e1c071b046e2b98848178fddf21552",
|
||||||
|
"reference": "34b2f0efa2e1c071b046e2b98848178fddf21552",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0",
|
||||||
|
"phrity/net-uri": "^1.0",
|
||||||
|
"phrity/util-errorhandler": "^1.0",
|
||||||
|
"psr/http-message": "^1.0",
|
||||||
|
"psr/log": "^1.0 | ^2.0 | ^3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
},
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"WebSocket\\": "lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"ISC"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fredrik Liljegren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "WebSocket client and server",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Textalk/websocket-php/issues",
|
||||||
|
"source": "https://github.com/Textalk/websocket-php/tree/master"
|
||||||
|
},
|
||||||
|
"time": "2023-12-16T14:43:30+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"stability-flags": {
|
||||||
|
"textalk/websocket": 20
|
||||||
|
},
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.6.0"
|
||||||
|
}
|
70
src/Monitor.php
Normal file
70
src/Monitor.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
namespace Laysense\Monitor;
|
||||||
|
|
||||||
|
use WebSocket\Client;
|
||||||
|
|
||||||
|
class Detection
|
||||||
|
{
|
||||||
|
var $dest='ws://172.83.153.167:2345';
|
||||||
|
var $nodeinfo;
|
||||||
|
public function getNode()
|
||||||
|
{
|
||||||
|
$nodeinfo = $this->callWebSocet('{"msg":"getnodelist"}');
|
||||||
|
$this->nodeinfo=$nodeinfo;
|
||||||
|
}
|
||||||
|
public function ping($host,$count)
|
||||||
|
{
|
||||||
|
$ping = $this->callWebSocet('{"msg":"ping","target:'.$host.'","type":"ICMP","count":'.$count.'}');
|
||||||
|
foreach($this->nodeinfo as $node){
|
||||||
|
$ping[$node->nodeid]['nodeinfo']=$node;
|
||||||
|
}
|
||||||
|
$this->ping=$ping;
|
||||||
|
}
|
||||||
|
public function getDelay()
|
||||||
|
{
|
||||||
|
$alldelay = 0;
|
||||||
|
foreach($this->ping as $id=>$node){
|
||||||
|
if((!isset($node['nodeinfo'])) || (!isset($node['delay'])) || $node['delay']<=10){
|
||||||
|
unset($this->ping[$id]);
|
||||||
|
}else{
|
||||||
|
$alldelay += $node['delay'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$alldelay = $alldelay/count($this->ping);
|
||||||
|
return $alldelay;
|
||||||
|
}
|
||||||
|
private function callWebSocet(){
|
||||||
|
$client = new Client($this->dest);
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
$client->send($msg);
|
||||||
|
|
||||||
|
// 接收消息
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
$receive=$client->receive();
|
||||||
|
$receive = json_decode($receive);
|
||||||
|
if($receive->msg="setnodes"&&isset($receive->nodes)){
|
||||||
|
$response = $receive->nodes;
|
||||||
|
break;
|
||||||
|
}elseif(isset($receive->delay)){
|
||||||
|
$response[$receive->nodeid]['delay'] = $receive->delay/1000;
|
||||||
|
}else{
|
||||||
|
$response[$receive->nodeid]['ip'] = $receive->ip;
|
||||||
|
$response[$receive->nodeid]['loc'] = $receive->loc;
|
||||||
|
$response[$receive->nodeid]['asn'] = $receive->asn;
|
||||||
|
$response[$receive->nodeid]['org'] = $receive->org;
|
||||||
|
}
|
||||||
|
} catch (\WebSocket\ConnectionException $e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭连接
|
||||||
|
$client->close();
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
vendor/autoload.php
vendored
Normal file
25
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trigger_error(
|
||||||
|
$err,
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit7f9863daaab5193cdcf774ddaa946c8d::getLoader();
|
579
vendor/composer/ClassLoader.php
vendored
Normal file
579
vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
359
vendor/composer/InstalledVersions.php
vendored
Normal file
359
vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||||
|
self::$installed = $installed[count($installed) - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array()) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
21
vendor/composer/LICENSE
vendored
Normal file
21
vendor/composer/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
10
vendor/composer/autoload_classmap.php
vendored
Normal file
10
vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
);
|
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
9
vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
15
vendor/composer/autoload_psr4.php
vendored
Normal file
15
vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'WebSocket\\' => array($vendorDir . '/textalk/websocket/lib'),
|
||||||
|
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
|
||||||
|
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
|
||||||
|
'Phrity\\Util\\' => array($vendorDir . '/phrity/util-errorhandler/src'),
|
||||||
|
'Phrity\\Net\\' => array($vendorDir . '/phrity/net-uri/src'),
|
||||||
|
'Laysense\\Monitor\\' => array($baseDir . '/src'),
|
||||||
|
);
|
38
vendor/composer/autoload_real.php
vendored
Normal file
38
vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit7f9863daaab5193cdcf774ddaa946c8d
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Autoload\ClassLoader
|
||||||
|
*/
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit7f9863daaab5193cdcf774ddaa946c8d', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit7f9863daaab5193cdcf774ddaa946c8d', 'loadClassLoader'));
|
||||||
|
|
||||||
|
require __DIR__ . '/autoload_static.php';
|
||||||
|
call_user_func(\Composer\Autoload\ComposerStaticInit7f9863daaab5193cdcf774ddaa946c8d::getInitializer($loader));
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
68
vendor/composer/autoload_static.php
vendored
Normal file
68
vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit7f9863daaab5193cdcf774ddaa946c8d
|
||||||
|
{
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'W' =>
|
||||||
|
array (
|
||||||
|
'WebSocket\\' => 10,
|
||||||
|
),
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'Psr\\Log\\' => 8,
|
||||||
|
'Psr\\Http\\Message\\' => 17,
|
||||||
|
'Phrity\\Util\\' => 12,
|
||||||
|
'Phrity\\Net\\' => 11,
|
||||||
|
),
|
||||||
|
'L' =>
|
||||||
|
array (
|
||||||
|
'Laysense\\Monitor\\' => 17,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'WebSocket\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/textalk/websocket/lib',
|
||||||
|
),
|
||||||
|
'Psr\\Log\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/psr/log/src',
|
||||||
|
),
|
||||||
|
'Psr\\Http\\Message\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/psr/http-factory/src',
|
||||||
|
1 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||||
|
),
|
||||||
|
'Phrity\\Util\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/phrity/util-errorhandler/src',
|
||||||
|
),
|
||||||
|
'Phrity\\Net\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/phrity/net-uri/src',
|
||||||
|
),
|
||||||
|
'Laysense\\Monitor\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/../..' . '/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $classMap = array (
|
||||||
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit7f9863daaab5193cdcf774ddaa946c8d::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit7f9863daaab5193cdcf774ddaa946c8d::$prefixDirsPsr4;
|
||||||
|
$loader->classMap = ComposerStaticInit7f9863daaab5193cdcf774ddaa946c8d::$classMap;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
378
vendor/composer/installed.json
vendored
Normal file
378
vendor/composer/installed.json
vendored
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "phrity/net-uri",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"version_normalized": "1.3.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sirn-se/phrity-net-uri.git",
|
||||||
|
"reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/3f458e0c4d1ddc0e218d7a5b9420127c63925f43",
|
||||||
|
"reference": "3f458e0c4d1ddc0e218d7a5b9420127c63925f43",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0",
|
||||||
|
"psr/http-factory": "^1.0",
|
||||||
|
"psr/http-message": "^1.0 | ^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.0 | ^10.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.0"
|
||||||
|
},
|
||||||
|
"time": "2023-08-21T10:33:06+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Net\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen",
|
||||||
|
"email": "sirn@sirn.se",
|
||||||
|
"homepage": "https://phrity.sirn.se"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PSR-7 Uri and PSR-17 UriFactory implementation",
|
||||||
|
"homepage": "https://phrity.sirn.se/net-uri",
|
||||||
|
"keywords": [
|
||||||
|
"psr-17",
|
||||||
|
"psr-7",
|
||||||
|
"uri",
|
||||||
|
"uri factory"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/sirn-se/phrity-net-uri/issues",
|
||||||
|
"source": "https://github.com/sirn-se/phrity-net-uri/tree/1.3.0"
|
||||||
|
},
|
||||||
|
"install-path": "../phrity/net-uri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phrity/util-errorhandler",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"version_normalized": "1.1.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/sirn-se/phrity-util-errorhandler.git",
|
||||||
|
"reference": "483228156e06673963902b1cc1e6bd9541ab4d5e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/483228156e06673963902b1cc1e6bd9541ab4d5e",
|
||||||
|
"reference": "483228156e06673963902b1cc1e6bd9541ab4d5e",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.0 | ^10.0 | ^11.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
},
|
||||||
|
"time": "2024-09-12T06:49:16+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Util\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen",
|
||||||
|
"email": "sirn@sirn.se",
|
||||||
|
"homepage": "https://phrity.sirn.se"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Inline error handler; catch and resolve errors for code block.",
|
||||||
|
"homepage": "https://phrity.sirn.se/util-errorhandler",
|
||||||
|
"keywords": [
|
||||||
|
"error",
|
||||||
|
"warning"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues",
|
||||||
|
"source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.1.1"
|
||||||
|
},
|
||||||
|
"install-path": "../phrity/util-errorhandler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-factory",
|
||||||
|
"version": "dev-master",
|
||||||
|
"version_normalized": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-factory.git",
|
||||||
|
"reference": "7037f4b0950474e9d1350e8df89b15f1842085f6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/7037f4b0950474e9d1350e8df89b15f1842085f6",
|
||||||
|
"reference": "7037f4b0950474e9d1350e8df89b15f1842085f6",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.0.0",
|
||||||
|
"psr/http-message": "^1.0 || ^2.0"
|
||||||
|
},
|
||||||
|
"time": "2023-09-22T11:16:44+00:00",
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||||
|
"keywords": [
|
||||||
|
"factory",
|
||||||
|
"http",
|
||||||
|
"message",
|
||||||
|
"psr",
|
||||||
|
"psr-17",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-factory"
|
||||||
|
},
|
||||||
|
"install-path": "../psr/http-factory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-message",
|
||||||
|
"version": "1.1",
|
||||||
|
"version_normalized": "1.1.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-message.git",
|
||||||
|
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||||
|
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"time": "2023-04-04T09:50:52+00:00",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.1.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for HTTP messages",
|
||||||
|
"homepage": "https://github.com/php-fig/http-message",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"http-message",
|
||||||
|
"psr",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
||||||
|
},
|
||||||
|
"install-path": "../psr/http-message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"version": "dev-master",
|
||||||
|
"version_normalized": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/log.git",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"time": "2024-09-11T13:17:53+00:00",
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"psr",
|
||||||
|
"psr-3"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/log/tree/3.0.2"
|
||||||
|
},
|
||||||
|
"install-path": "../psr/log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "textalk/websocket",
|
||||||
|
"version": "dev-master",
|
||||||
|
"version_normalized": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Textalk/websocket-php.git",
|
||||||
|
"reference": "34b2f0efa2e1c071b046e2b98848178fddf21552"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/34b2f0efa2e1c071b046e2b98848178fddf21552",
|
||||||
|
"reference": "34b2f0efa2e1c071b046e2b98848178fddf21552",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0",
|
||||||
|
"phrity/net-uri": "^1.0",
|
||||||
|
"phrity/util-errorhandler": "^1.0",
|
||||||
|
"psr/http-message": "^1.0",
|
||||||
|
"psr/log": "^1.0 | ^2.0 | ^3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
},
|
||||||
|
"time": "2023-12-16T14:43:30+00:00",
|
||||||
|
"default-branch": true,
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"WebSocket\\": "lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"ISC"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fredrik Liljegren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "WebSocket client and server",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Textalk/websocket-php/issues",
|
||||||
|
"source": "https://github.com/Textalk/websocket-php/tree/master"
|
||||||
|
},
|
||||||
|
"install-path": "../textalk/websocket"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"dev-package-names": []
|
||||||
|
}
|
83
vendor/composer/installed.php
vendored
Normal file
83
vendor/composer/installed.php
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php return array(
|
||||||
|
'root' => array(
|
||||||
|
'name' => 'laysense/monitor',
|
||||||
|
'pretty_version' => '1.0.0+no-version-set',
|
||||||
|
'version' => '1.0.0.0',
|
||||||
|
'reference' => null,
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev' => true,
|
||||||
|
),
|
||||||
|
'versions' => array(
|
||||||
|
'laysense/monitor' => array(
|
||||||
|
'pretty_version' => '1.0.0+no-version-set',
|
||||||
|
'version' => '1.0.0.0',
|
||||||
|
'reference' => null,
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../../',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'phrity/net-uri' => array(
|
||||||
|
'pretty_version' => '1.3.0',
|
||||||
|
'version' => '1.3.0.0',
|
||||||
|
'reference' => '3f458e0c4d1ddc0e218d7a5b9420127c63925f43',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phrity/net-uri',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'phrity/util-errorhandler' => array(
|
||||||
|
'pretty_version' => '1.1.1',
|
||||||
|
'version' => '1.1.1.0',
|
||||||
|
'reference' => '483228156e06673963902b1cc1e6bd9541ab4d5e',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../phrity/util-errorhandler',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'psr/http-factory' => array(
|
||||||
|
'pretty_version' => 'dev-master',
|
||||||
|
'version' => 'dev-master',
|
||||||
|
'reference' => '7037f4b0950474e9d1350e8df89b15f1842085f6',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../psr/http-factory',
|
||||||
|
'aliases' => array(
|
||||||
|
0 => '1.0.x-dev',
|
||||||
|
),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'psr/http-message' => array(
|
||||||
|
'pretty_version' => '1.1',
|
||||||
|
'version' => '1.1.0.0',
|
||||||
|
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../psr/http-message',
|
||||||
|
'aliases' => array(),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'psr/log' => array(
|
||||||
|
'pretty_version' => 'dev-master',
|
||||||
|
'version' => 'dev-master',
|
||||||
|
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../psr/log',
|
||||||
|
'aliases' => array(
|
||||||
|
0 => '3.x-dev',
|
||||||
|
),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
'textalk/websocket' => array(
|
||||||
|
'pretty_version' => 'dev-master',
|
||||||
|
'version' => 'dev-master',
|
||||||
|
'reference' => '34b2f0efa2e1c071b046e2b98848178fddf21552',
|
||||||
|
'type' => 'library',
|
||||||
|
'install_path' => __DIR__ . '/../textalk/websocket',
|
||||||
|
'aliases' => array(
|
||||||
|
0 => '9999999-dev',
|
||||||
|
),
|
||||||
|
'dev_requirement' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
26
vendor/composer/platform_check.php
vendored
Normal file
26
vendor/composer/platform_check.php
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// platform_check.php @generated by Composer
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
|
||||||
|
if (!(PHP_VERSION_ID >= 80000)) {
|
||||||
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($issues) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trigger_error(
|
||||||
|
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
30
vendor/phrity/net-uri/composer.json
vendored
Normal file
30
vendor/phrity/net-uri/composer.json
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "phrity/net-uri",
|
||||||
|
"type": "library",
|
||||||
|
"description": "PSR-7 Uri and PSR-17 UriFactory implementation",
|
||||||
|
"homepage": "https://phrity.sirn.se/net-uri",
|
||||||
|
"keywords": ["uri", "uri factory", "PSR-7", "PSR-17"],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen",
|
||||||
|
"email": "sirn@sirn.se",
|
||||||
|
"homepage": "https://phrity.sirn.se"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Net\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0",
|
||||||
|
"psr/http-factory": "^1.0",
|
||||||
|
"psr/http-message": "^1.0 | ^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.0 | ^10.0",
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.0"
|
||||||
|
}
|
||||||
|
}
|
486
vendor/phrity/net-uri/src/Uri.php
vendored
Normal file
486
vendor/phrity/net-uri/src/Uri.php
vendored
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File for Net\Uri class.
|
||||||
|
* @package Phrity > Net > Uri
|
||||||
|
* @see https://www.rfc-editor.org/rfc/rfc3986
|
||||||
|
* @see https://www.php-fig.org/psr/psr-7/#35-psrhttpmessageuriinterface
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Phrity\Net;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Net\Uri class.
|
||||||
|
*/
|
||||||
|
class Uri implements UriInterface
|
||||||
|
{
|
||||||
|
public const REQUIRE_PORT = 1; // Always include port, explicit or default
|
||||||
|
public const ABSOLUTE_PATH = 2; // Enforce absolute path
|
||||||
|
public const NORMALIZE_PATH = 4; // Normalize path
|
||||||
|
public const IDNA = 8; // IDNA-convert host
|
||||||
|
|
||||||
|
private const RE_MAIN = '!^(?P<schemec>(?P<scheme>[^:/?#]+):)?(?P<authorityc>//(?P<authority>[^/?#]*))?'
|
||||||
|
. '(?P<path>[^?#]*)(?P<queryc>\?(?P<query>[^#]*))?(?P<fragmentc>#(?P<fragment>.*))?$!';
|
||||||
|
private const RE_AUTH = '!^(?P<userinfoc>(?P<user>[^:/?#]+)(?P<passc>:(?P<pass>[^:/?#]+))?@)?'
|
||||||
|
. '(?P<host>[^:/?#]*|\[[^/?#]*\])(?P<portc>:(?P<port>[0-9]*))?$!';
|
||||||
|
|
||||||
|
private static $port_defaults = [
|
||||||
|
'acap' => 674,
|
||||||
|
'afp' => 548,
|
||||||
|
'dict' => 2628,
|
||||||
|
'dns' => 53,
|
||||||
|
'ftp' => 21,
|
||||||
|
'git' => 9418,
|
||||||
|
'gopher' => 70,
|
||||||
|
'http' => 80,
|
||||||
|
'https' => 443,
|
||||||
|
'imap' => 143,
|
||||||
|
'ipp' => 631,
|
||||||
|
'ipps' => 631,
|
||||||
|
'irc' => 194,
|
||||||
|
'ircs' => 6697,
|
||||||
|
'ldap' => 389,
|
||||||
|
'ldaps' => 636,
|
||||||
|
'mms' => 1755,
|
||||||
|
'msrp' => 2855,
|
||||||
|
'mtqp' => 1038,
|
||||||
|
'nfs' => 111,
|
||||||
|
'nntp' => 119,
|
||||||
|
'nntps' => 563,
|
||||||
|
'pop' => 110,
|
||||||
|
'prospero' => 1525,
|
||||||
|
'redis' => 6379,
|
||||||
|
'rsync' => 873,
|
||||||
|
'rtsp' => 554,
|
||||||
|
'rtsps' => 322,
|
||||||
|
'rtspu' => 5005,
|
||||||
|
'sftp' => 22,
|
||||||
|
'smb' => 445,
|
||||||
|
'snmp' => 161,
|
||||||
|
'ssh' => 22,
|
||||||
|
'svn' => 3690,
|
||||||
|
'telnet' => 23,
|
||||||
|
'ventrilo' => 3784,
|
||||||
|
'vnc' => 5900,
|
||||||
|
'wais' => 210,
|
||||||
|
'ws' => 80,
|
||||||
|
'wss' => 443,
|
||||||
|
];
|
||||||
|
|
||||||
|
private $scheme;
|
||||||
|
private $authority;
|
||||||
|
private $host;
|
||||||
|
private $port;
|
||||||
|
private $user;
|
||||||
|
private $pass;
|
||||||
|
private $path;
|
||||||
|
private $query;
|
||||||
|
private $fragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new URI instance using a string
|
||||||
|
* @param string $uri_string URI as string
|
||||||
|
* @throws \InvalidArgumentException If the given URI cannot be parsed
|
||||||
|
*/
|
||||||
|
public function __construct(string $uri_string = '', int $flags = 0)
|
||||||
|
{
|
||||||
|
$this->parse($uri_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- PSR-7 getters ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the scheme component of the URI.
|
||||||
|
* @return string The URI scheme
|
||||||
|
*/
|
||||||
|
public function getScheme(int $flags = 0): string
|
||||||
|
{
|
||||||
|
return $this->getComponent('scheme') ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the authority component of the URI.
|
||||||
|
* @return string The URI authority, in "[user-info@]host[:port]" format
|
||||||
|
*/
|
||||||
|
public function getAuthority(int $flags = 0): string
|
||||||
|
{
|
||||||
|
$host = $this->formatComponent($this->getHost($flags));
|
||||||
|
if ($this->isEmpty($host)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$userinfo = $this->formatComponent($this->getUserInfo(), '', '@');
|
||||||
|
$port = $this->formatComponent($this->getPort($flags), ':');
|
||||||
|
return "{$userinfo}{$host}{$port}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the user information component of the URI.
|
||||||
|
* @return string The URI user information, in "username[:password]" format
|
||||||
|
*/
|
||||||
|
public function getUserInfo(int $flags = 0): string
|
||||||
|
{
|
||||||
|
$user = $this->formatComponent($this->getComponent('user'));
|
||||||
|
$pass = $this->formatComponent($this->getComponent('pass'), ':');
|
||||||
|
return $this->isEmpty($user) ? '' : "{$user}{$pass}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the host component of the URI.
|
||||||
|
* @return string The URI host
|
||||||
|
*/
|
||||||
|
public function getHost(int $flags = 0): string
|
||||||
|
{
|
||||||
|
$host = $this->getComponent('host') ?? '';
|
||||||
|
if ($flags & self::IDNA) {
|
||||||
|
$host = $this->idna($host);
|
||||||
|
}
|
||||||
|
return $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the port component of the URI.
|
||||||
|
* @return null|int The URI port
|
||||||
|
*/
|
||||||
|
public function getPort(int $flags = 0): ?int
|
||||||
|
{
|
||||||
|
$port = $this->getComponent('port');
|
||||||
|
$scheme = $this->getComponent('scheme');
|
||||||
|
$default = isset(self::$port_defaults[$scheme]) ? self::$port_defaults[$scheme] : null;
|
||||||
|
if ($flags & self::REQUIRE_PORT) {
|
||||||
|
return !$this->isEmpty($port) ? $port : $default;
|
||||||
|
}
|
||||||
|
return $this->isEmpty($port) || $port === $default ? null : $port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the path component of the URI.
|
||||||
|
* @return string The URI path
|
||||||
|
*/
|
||||||
|
public function getPath(int $flags = 0): string
|
||||||
|
{
|
||||||
|
$path = $this->getComponent('path') ?? '';
|
||||||
|
if ($flags & self::NORMALIZE_PATH) {
|
||||||
|
$path = $this->normalizePath($path);
|
||||||
|
}
|
||||||
|
if ($flags & self::ABSOLUTE_PATH && substr($path, 0, 1) !== '/') {
|
||||||
|
$path = "/{$path}";
|
||||||
|
}
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the query string of the URI.
|
||||||
|
* @return string The URI query string
|
||||||
|
*/
|
||||||
|
public function getQuery(int $flags = 0): string
|
||||||
|
{
|
||||||
|
return $this->getComponent('query') ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the fragment component of the URI.
|
||||||
|
* @return string The URI fragment
|
||||||
|
*/
|
||||||
|
public function getFragment(int $flags = 0): string
|
||||||
|
{
|
||||||
|
return $this->getComponent('fragment') ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- PSR-7 setters ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified scheme.
|
||||||
|
* @param string $scheme The scheme to use with the new instance
|
||||||
|
* @return static A new instance with the specified scheme
|
||||||
|
* @throws \InvalidArgumentException for invalid schemes
|
||||||
|
* @throws \InvalidArgumentException for unsupported schemes
|
||||||
|
*/
|
||||||
|
public function withScheme($scheme, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
if ($flags & self::REQUIRE_PORT) {
|
||||||
|
$clone->setComponent('port', $this->getPort(self::REQUIRE_PORT));
|
||||||
|
$default = isset(self::$port_defaults[$scheme]) ? self::$port_defaults[$scheme] : null;
|
||||||
|
}
|
||||||
|
$clone->setComponent('scheme', $scheme);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified user information.
|
||||||
|
* @param string $user The user name to use for authority
|
||||||
|
* @param null|string $password The password associated with $user
|
||||||
|
* @return static A new instance with the specified user information
|
||||||
|
*/
|
||||||
|
public function withUserInfo($user, $password = null, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->setComponent('user', $user);
|
||||||
|
$clone->setComponent('pass', $password);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified host.
|
||||||
|
* @param string $host The hostname to use with the new instance
|
||||||
|
* @return static A new instance with the specified host
|
||||||
|
* @throws \InvalidArgumentException for invalid hostnames
|
||||||
|
*/
|
||||||
|
public function withHost($host, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
if ($flags & self::IDNA) {
|
||||||
|
$host = $this->idna($host);
|
||||||
|
}
|
||||||
|
$clone->setComponent('host', $host);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified port.
|
||||||
|
* @param null|int $port The port to use with the new instance
|
||||||
|
* @return static A new instance with the specified port
|
||||||
|
* @throws \InvalidArgumentException for invalid ports
|
||||||
|
*/
|
||||||
|
public function withPort($port, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->setComponent('port', $port);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified path.
|
||||||
|
* @param string $path The path to use with the new instance
|
||||||
|
* @return static A new instance with the specified path
|
||||||
|
* @throws \InvalidArgumentException for invalid paths
|
||||||
|
*/
|
||||||
|
public function withPath($path, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
if ($flags & self::NORMALIZE_PATH) {
|
||||||
|
$path = $this->normalizePath($path);
|
||||||
|
}
|
||||||
|
if ($flags & self::ABSOLUTE_PATH && substr($path, 0, 1) !== '/') {
|
||||||
|
$path = "/{$path}";
|
||||||
|
}
|
||||||
|
$clone->setComponent('path', $path);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified query string.
|
||||||
|
* @param string $query The query string to use with the new instance
|
||||||
|
* @return static A new instance with the specified query string
|
||||||
|
* @throws \InvalidArgumentException for invalid query strings
|
||||||
|
*/
|
||||||
|
public function withQuery($query, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->setComponent('query', $query);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified URI fragment.
|
||||||
|
* @param string $fragment The fragment to use with the new instance
|
||||||
|
* @return static A new instance with the specified fragment
|
||||||
|
*/
|
||||||
|
public function withFragment($fragment, int $flags = 0): UriInterface
|
||||||
|
{
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->setComponent('fragment', $fragment);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- PSR-7 string ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string representation as a URI reference.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- Extensions ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string representation as a URI reference.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function toString(int $flags = 0): string
|
||||||
|
{
|
||||||
|
$scheme = $this->formatComponent($this->getComponent('scheme'), '', ':');
|
||||||
|
$authority = $this->authority ? "//{$this->formatComponent($this->getAuthority($flags))}" : '';
|
||||||
|
$path_flags = ($this->authority && $this->path ? self::ABSOLUTE_PATH : 0) | $flags;
|
||||||
|
$path = $this->formatComponent($this->getPath($path_flags));
|
||||||
|
$query = $this->formatComponent($this->getComponent('query'), '?');
|
||||||
|
$fragment = $this->formatComponent($this->getComponent('fragment'), '#');
|
||||||
|
return "{$scheme}{$authority}{$path}{$query}{$fragment}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- Private helper methods ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private function parse(string $uri_string = ''): void
|
||||||
|
{
|
||||||
|
if ($uri_string === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
preg_match(self::RE_MAIN, $uri_string, $main);
|
||||||
|
$this->authority = !empty($main['authorityc']);
|
||||||
|
$this->setComponent('scheme', isset($main['schemec']) ? $main['scheme'] : '');
|
||||||
|
$this->setComponent('path', isset($main['path']) ? $main['path'] : '');
|
||||||
|
$this->setComponent('query', isset($main['queryc']) ? $main['query'] : '');
|
||||||
|
$this->setComponent('fragment', isset($main['fragmentc']) ? $main['fragment'] : '');
|
||||||
|
if ($this->authority) {
|
||||||
|
preg_match(self::RE_AUTH, $main['authority'], $auth);
|
||||||
|
if (empty($auth) && $main['authority'] !== '') {
|
||||||
|
throw new InvalidArgumentException("Invalid 'authority'.");
|
||||||
|
}
|
||||||
|
if ($this->isEmpty($auth['host']) && !$this->isEmpty($auth['user'])) {
|
||||||
|
throw new InvalidArgumentException("Invalid 'authority'.");
|
||||||
|
}
|
||||||
|
$this->setComponent('user', isset($auth['user']) ? $auth['user'] : '');
|
||||||
|
$this->setComponent('pass', isset($auth['passc']) ? $auth['pass'] : '');
|
||||||
|
$this->setComponent('host', isset($auth['host']) ? $auth['host'] : '');
|
||||||
|
$this->setComponent('port', isset($auth['portc']) ? $auth['port'] : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function encode(string $source, string $keep = ''): string
|
||||||
|
{
|
||||||
|
$exclude = "[^%\/:=&!\$'()*+,;@{$keep}]+";
|
||||||
|
$exp = "/(%{$exclude})|({$exclude})/";
|
||||||
|
return preg_replace_callback($exp, function ($matches) {
|
||||||
|
if ($e = preg_match('/^(%[0-9a-fA-F]{2})/', $matches[0], $m)) {
|
||||||
|
return substr($matches[0], 0, 3) . rawurlencode(substr($matches[0], 3));
|
||||||
|
} else {
|
||||||
|
return rawurlencode($matches[0]);
|
||||||
|
}
|
||||||
|
}, $source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setComponent(string $component, $value): void
|
||||||
|
{
|
||||||
|
$value = $this->parseCompontent($component, $value);
|
||||||
|
$this->$component = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseCompontent(string $component, $value)
|
||||||
|
{
|
||||||
|
if ($this->isEmpty($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch ($component) {
|
||||||
|
case 'scheme':
|
||||||
|
$this->assertString($component, $value);
|
||||||
|
$this->assertpattern($component, $value, '/^[a-z][a-z0-9-+.]*$/i');
|
||||||
|
return mb_strtolower($value);
|
||||||
|
case 'host': // IP-literal / IPv4address / reg-name
|
||||||
|
$this->assertString($component, $value);
|
||||||
|
$this->authority = $this->authority || !$this->isEmpty($value);
|
||||||
|
return mb_strtolower($value);
|
||||||
|
case 'port':
|
||||||
|
$this->assertInteger($component, $value);
|
||||||
|
if ($value < 0 || $value > 65535) {
|
||||||
|
throw new InvalidArgumentException("Invalid port number");
|
||||||
|
}
|
||||||
|
return (int)$value;
|
||||||
|
case 'path':
|
||||||
|
$this->assertString($component, $value);
|
||||||
|
$value = $this->encode($value);
|
||||||
|
return $value;
|
||||||
|
case 'user':
|
||||||
|
case 'pass':
|
||||||
|
case 'query':
|
||||||
|
case 'fragment':
|
||||||
|
$this->assertString($component, $value);
|
||||||
|
$value = $this->encode($value, '?');
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getComponent(string $component)
|
||||||
|
{
|
||||||
|
return isset($this->$component) ? $this->$component : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatComponent($value, string $before = '', string $after = ''): string
|
||||||
|
{
|
||||||
|
return $this->isEmpty($value) ? '' : "{$before}{$value}{$after}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isEmpty($value): bool
|
||||||
|
{
|
||||||
|
return is_null($value) || $value === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertString(string $component, $value): void
|
||||||
|
{
|
||||||
|
if (!is_string($value)) {
|
||||||
|
throw new InvalidArgumentException("Invalid '{$component}': Should be a string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertInteger(string $component, $value): void
|
||||||
|
{
|
||||||
|
if (!is_numeric($value) || intval($value) != $value) {
|
||||||
|
throw new InvalidArgumentException("Invalid '{$component}': Should be an integer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertPattern(string $component, string $value, string $pattern): void
|
||||||
|
{
|
||||||
|
if (preg_match($pattern, $value) == 0) {
|
||||||
|
throw new InvalidArgumentException("Invalid '{$component}': Should match {$pattern}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizePath(string $path): string
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
preg_match_all('!([^/]*/|[^/]*$)!', $path, $items);
|
||||||
|
foreach ($items[0] as $item) {
|
||||||
|
switch ($item) {
|
||||||
|
case '':
|
||||||
|
case './':
|
||||||
|
case '.':
|
||||||
|
break; // just skip
|
||||||
|
case '/':
|
||||||
|
if (empty($result)) {
|
||||||
|
array_push($result, $item); // add
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '..':
|
||||||
|
case '../':
|
||||||
|
if (empty($result) || end($result) == '../') {
|
||||||
|
array_push($result, $item); // add
|
||||||
|
} else {
|
||||||
|
array_pop($result); // remove previous
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
array_push($result, $item); // add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode('', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function idna(string $value): string
|
||||||
|
{
|
||||||
|
if ($value === '' || !is_callable('idn_to_ascii')) {
|
||||||
|
return $value; // Can't convert, but don't cause exception
|
||||||
|
}
|
||||||
|
return idn_to_ascii($value, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
}
|
||||||
|
}
|
31
vendor/phrity/net-uri/src/UriFactory.php
vendored
Normal file
31
vendor/phrity/net-uri/src/UriFactory.php
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File for Net\UriFactory class.
|
||||||
|
* @package Phrity > Net > Uri
|
||||||
|
* @see https://www.rfc-editor.org/rfc/rfc3986
|
||||||
|
* @see https://www.php-fig.org/psr/psr-17/#26-urifactoryinterface
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Phrity\Net;
|
||||||
|
|
||||||
|
use Psr\Http\Message\{
|
||||||
|
UriFactoryInterface,
|
||||||
|
UriInterface
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Net\UriFactory class.
|
||||||
|
*/
|
||||||
|
class UriFactory implements UriFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new URI.
|
||||||
|
* @param string $uri The URI to parse.
|
||||||
|
* @throws \InvalidArgumentException If the given URI cannot be parsed
|
||||||
|
*/
|
||||||
|
public function createUri(string $uri = ''): UriInterface
|
||||||
|
{
|
||||||
|
return new Uri($uri);
|
||||||
|
}
|
||||||
|
}
|
93
vendor/phrity/util-errorhandler/.github/workflows/acceptance.yml
vendored
Normal file
93
vendor/phrity/util-errorhandler/.github/workflows/acceptance.yml
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
name: Acceptance
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
test:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: ["7.4", "8.0", "8.1", "8.2", "8.3", "8.4"]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Unit test
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php-versions }}
|
||||||
|
coverage: none
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Get composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
|
||||||
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: composer install --prefer-dist
|
||||||
|
- name: Test
|
||||||
|
run: vendor/bin/phpunit
|
||||||
|
|
||||||
|
cs-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Code standard
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "8.3"
|
||||||
|
coverage: none
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Get composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
|
||||||
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: composer install --prefer-dist
|
||||||
|
- name: Code standard
|
||||||
|
run: vendor/bin/phpcs --standard=PSR1,PSR12 --encoding=UTF-8 --report=full --colors src tests
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Code coverage
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "8.3"
|
||||||
|
coverage: xdebug
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Get composer cache directory
|
||||||
|
id: composer-cache
|
||||||
|
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
|
||||||
|
restore-keys: ${{ runner.os }}-composer-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: composer install --prefer-dist
|
||||||
|
- name: Code coverage build
|
||||||
|
run: XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||||
|
- name: Code coverage upload
|
||||||
|
env:
|
||||||
|
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: vendor/bin/php-coveralls -v
|
6
vendor/phrity/util-errorhandler/.gitignore
vendored
Normal file
6
vendor/phrity/util-errorhandler/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.DS_Store
|
||||||
|
.phpunit.result.cache
|
||||||
|
build/
|
||||||
|
composer.lock
|
||||||
|
composer.phar
|
||||||
|
vendor/
|
41
vendor/phrity/util-errorhandler/Makefile
vendored
Normal file
41
vendor/phrity/util-errorhandler/Makefile
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Default
|
||||||
|
all: deps-install
|
||||||
|
|
||||||
|
|
||||||
|
# DEPENDENCY MANAGEMENT
|
||||||
|
|
||||||
|
# Updates dependencies according to lock file
|
||||||
|
deps-install: composer.phar
|
||||||
|
./composer.phar --no-interaction install
|
||||||
|
|
||||||
|
# Updates dependencies according to json file
|
||||||
|
deps-update: composer.phar
|
||||||
|
./composer.phar self-update
|
||||||
|
./composer.phar --no-interaction update
|
||||||
|
|
||||||
|
|
||||||
|
# TESTS AND REPORTS
|
||||||
|
|
||||||
|
# Code standard check
|
||||||
|
cs-check: composer.lock
|
||||||
|
./vendor/bin/phpcs --standard=PSR1,PSR12 --encoding=UTF-8 --report=full --colors src tests
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
test: composer.lock
|
||||||
|
./vendor/bin/phpunit
|
||||||
|
|
||||||
|
# Run tests with clover coverage report
|
||||||
|
coverage: composer.lock
|
||||||
|
XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||||
|
./vendor/bin/php-coveralls -v
|
||||||
|
|
||||||
|
|
||||||
|
# INITIAL INSTALL
|
||||||
|
|
||||||
|
# Ensures composer is installed
|
||||||
|
composer.phar:
|
||||||
|
curl -sS https://getcomposer.org/installer | php
|
||||||
|
|
||||||
|
# Ensures composer is installed and dependencies loaded
|
||||||
|
composer.lock: composer.phar
|
||||||
|
./composer.phar --no-interaction install
|
147
vendor/phrity/util-errorhandler/README.md
vendored
Normal file
147
vendor/phrity/util-errorhandler/README.md
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
[![Build Status](https://github.com/sirn-se/phrity-util-errorhandler/actions/workflows/acceptance.yml/badge.svg)](https://github.com/sirn-se/phrity-util-errorhandler/actions)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/sirn-se/phrity-util-errorhandler/badge.svg?branch=main)](https://coveralls.io/github/sirn-se/phrity-util-errorhandler?branch=main)
|
||||||
|
|
||||||
|
# Error Handler utility
|
||||||
|
|
||||||
|
The PHP [error handling](https://www.php.net/manual/en/book.errorfunc.php) can be somewhat of a headache.
|
||||||
|
Typically an application uses a system level [error handler](https://www.php.net/manual/en/function.set-error-handler.php) and/or suppressing errors using the `@` prefix.
|
||||||
|
But those cases when your code need to act on triggered errors are more tricky.
|
||||||
|
|
||||||
|
This library provides two convenience methods to handle errors on code blocks, either by throwing exceptions or running callback code when an error occurs.
|
||||||
|
|
||||||
|
Current version supports PHP `^7.2|^8.0`.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install with [Composer](https://getcomposer.org/);
|
||||||
|
```
|
||||||
|
composer require phrity/util-errorhandler
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Error Handler
|
||||||
|
|
||||||
|
The class provides two main methods; `with()` and `withAll()`.
|
||||||
|
The difference is that `with()` will act immediately on an error and abort further code execution, while `withAll()` will attempt to execute the entire code block before acting on errors that occurred.
|
||||||
|
|
||||||
|
### Throwing ErrorException
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
});
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
The examples above will run the callback code, but if an error occurs it will throw an [ErrorException](https://www.php.net/manual/en/class.errorexception.php).
|
||||||
|
Error message and severity will be that of the triggering error.
|
||||||
|
* `with()` will throw immediately when occured
|
||||||
|
* `withAll()` will throw when code is complete; if more than one error occurred, the first will be thrown
|
||||||
|
|
||||||
|
### Throwing specified Throwable
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
}, new RuntimeException('A specified error'));
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
}, new RuntimeException('A specified error'));
|
||||||
|
```
|
||||||
|
The examples above will run the callback code, but if an error occurs it will throw provided Throwable.
|
||||||
|
The thrown Throwable will have an [ErrorException](https://www.php.net/manual/en/class.errorexception.php) attached as `$previous`.
|
||||||
|
* `with()` will throw immediately when occured
|
||||||
|
* `withAll()` will throw when code is complete; if more than one error occurred, the first will be thrown
|
||||||
|
|
||||||
|
### Using callback
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
}, function (ErrorException $error) {
|
||||||
|
// Code to handle error
|
||||||
|
return $error_result;
|
||||||
|
});
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
}, function (array $errors, $success_result) {
|
||||||
|
// Code to handle errors
|
||||||
|
return $error_result;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
The examples above will run the callback code, but if an error occurs it will call the error callback as well.
|
||||||
|
* `with()` will run the error callback immediately when error occured; error callback expects an ErrorException instance
|
||||||
|
* `withAll()` will run the error callback when code is complete; error callback expects an array of ErrorException and the returned result of code callback
|
||||||
|
|
||||||
|
### Filtering error types
|
||||||
|
|
||||||
|
Both `with()` and `withAll()` accepts error level(s) as last parameter.
|
||||||
|
```php
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
}, null, E_USER_ERROR);
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
// Code to execute
|
||||||
|
return $success_result;
|
||||||
|
}, null, E_USER_ERROR & E_USER_WARNING);
|
||||||
|
```
|
||||||
|
Any value or combination of values accepted by [set_error_handler](https://www.php.net/manual/en/function.set-error-handler.php) is usable.
|
||||||
|
Default is `E_ALL`. [List of constants](https://www.php.net/manual/en/errorfunc.constants.php).
|
||||||
|
|
||||||
|
### The global error handler
|
||||||
|
|
||||||
|
The class also has global `set()` and `restore()` methods.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$handler->set(); // Throws ErrorException on error
|
||||||
|
$handler->set(new RuntimeException('A specified error')); // Throws provided Throwable on error
|
||||||
|
$handler->set(function (ErrorException $error) {
|
||||||
|
// Code to handle errors
|
||||||
|
return $error_result;
|
||||||
|
}); // Runs callback on error
|
||||||
|
$handler->restore(); // Restores error handler
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class synopsis
|
||||||
|
|
||||||
|
```php
|
||||||
|
Phrity\Util\ErrorHandler {
|
||||||
|
|
||||||
|
/* Methods */
|
||||||
|
public __construct()
|
||||||
|
|
||||||
|
public with(callable $callback, mixed $handling = null, int $levels = E_ALL) : mixed
|
||||||
|
public withAll(callable $callback, mixed $handling = null, int $levels = E_ALL) : mixed
|
||||||
|
public set($handling = null, int $levels = E_ALL) : mixed
|
||||||
|
public restore() : bool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
| Version | PHP | |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `1.1` | `^7.4\|^8.0` | Some improvements |
|
||||||
|
| `1.0` | `^7.2\|^8.0` | Initial version |
|
33
vendor/phrity/util-errorhandler/composer.json
vendored
Normal file
33
vendor/phrity/util-errorhandler/composer.json
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "phrity/util-errorhandler",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Inline error handler; catch and resolve errors for code block.",
|
||||||
|
"homepage": "https://phrity.sirn.se/util-errorhandler",
|
||||||
|
"keywords": ["error", "warning"],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen",
|
||||||
|
"email": "sirn@sirn.se",
|
||||||
|
"homepage": "https://phrity.sirn.se"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Util\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Phrity\\Util\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.0 | ^10.0 | ^11.0",
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
}
|
||||||
|
}
|
13
vendor/phrity/util-errorhandler/phpunit.xml.dist
vendored
Normal file
13
vendor/phrity/util-errorhandler/phpunit.xml.dist
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Phrity Util/Accessor tests">
|
||||||
|
<directory>./tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">./src/</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
121
vendor/phrity/util-errorhandler/src/ErrorHandler.php
vendored
Normal file
121
vendor/phrity/util-errorhandler/src/ErrorHandler.php
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File for ErrorHandler utility class.
|
||||||
|
* @package Phrity > Util > ErrorHandler
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Phrity\Util;
|
||||||
|
|
||||||
|
use ErrorException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ErrorHandler utility class.
|
||||||
|
* Allows catching and resolving errors inline.
|
||||||
|
*/
|
||||||
|
class ErrorHandler
|
||||||
|
{
|
||||||
|
/* ----------------- Public methods ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set error handler to run until removed.
|
||||||
|
* @param mixed $handling
|
||||||
|
* - If null, handler will throw ErrorException
|
||||||
|
* - If Throwable $t, throw $t with ErrorException attached as previous
|
||||||
|
* - If callable, will invoke callback with ErrorException as argument
|
||||||
|
* @param int $levels Error levels to catch, all errors by default
|
||||||
|
* @return mixed Previously registered error handler, if any
|
||||||
|
*/
|
||||||
|
public function set($handling = null, int $levels = E_ALL)
|
||||||
|
{
|
||||||
|
return set_error_handler($this->getHandler($handling), $levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove error handler.
|
||||||
|
* @return bool True if removed
|
||||||
|
*/
|
||||||
|
public function restore(): bool
|
||||||
|
{
|
||||||
|
return restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run code with error handling, breaks on first encountered error.
|
||||||
|
* @param callable $callback The code to run
|
||||||
|
* @param mixed $handling
|
||||||
|
* - If null, handler will throw ErrorException
|
||||||
|
* - If Throwable $t, throw $t with ErrorException attached as previous
|
||||||
|
* - If callable, will invoke callback with ErrorException as argument
|
||||||
|
* @param int $levels Error levels to catch, all errors by default
|
||||||
|
* @return mixed Return what $callback returns, or what $handling retuns on error
|
||||||
|
*/
|
||||||
|
public function with(callable $callback, $handling = null, int $levels = E_ALL)
|
||||||
|
{
|
||||||
|
$error = null;
|
||||||
|
$result = null;
|
||||||
|
try {
|
||||||
|
$this->set(null, $levels);
|
||||||
|
$result = $callback();
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$error = $this->handle($handling, $e);
|
||||||
|
} finally {
|
||||||
|
$this->restore();
|
||||||
|
}
|
||||||
|
return $error ?? $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run code with error handling, comletes code before handling errors
|
||||||
|
* @param callable $callback The code to run
|
||||||
|
* @param mixed $handling
|
||||||
|
* - If null, handler will throw ErrorException
|
||||||
|
* - If Throwable $t, throw $t with ErrorException attached as previous
|
||||||
|
* - If callable, will invoke callback with ErrorException as argument
|
||||||
|
* @param int $levels Error levels to catch, all errors by default
|
||||||
|
* @return mixed Return what $callback returns, or what $handling retuns on error
|
||||||
|
*/
|
||||||
|
public function withAll(callable $callback, $handling = null, int $levels = E_ALL)
|
||||||
|
{
|
||||||
|
$errors = [];
|
||||||
|
$this->set(function (ErrorException $e) use (&$errors) {
|
||||||
|
$errors[] = $e;
|
||||||
|
}, $levels);
|
||||||
|
$result = $callback();
|
||||||
|
$this->restore();
|
||||||
|
$error = empty($errors) ? null : $this->handle($handling, $errors, $result);
|
||||||
|
return $error ?? $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------- Private helpers --------------------------------------------- */
|
||||||
|
|
||||||
|
// Get handler function
|
||||||
|
private function getHandler($handling)
|
||||||
|
{
|
||||||
|
return function ($severity, $message, $file, $line) use ($handling) {
|
||||||
|
$error = new ErrorException($message, 0, $severity, $file, $line);
|
||||||
|
$this->handle($handling, $error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle error according to $handlig type
|
||||||
|
private function handle($handling, $error, $result = null)
|
||||||
|
{
|
||||||
|
if (is_callable($handling)) {
|
||||||
|
return $handling($error, $result);
|
||||||
|
}
|
||||||
|
if (is_array($error)) {
|
||||||
|
$error = array_shift($error);
|
||||||
|
}
|
||||||
|
if ($handling instanceof Throwable) {
|
||||||
|
try {
|
||||||
|
throw $error;
|
||||||
|
} finally {
|
||||||
|
throw $handling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
303
vendor/phrity/util-errorhandler/tests/ErrorHandlerTest.php
vendored
Normal file
303
vendor/phrity/util-errorhandler/tests/ErrorHandlerTest.php
vendored
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File for ErrorHandler function tests.
|
||||||
|
* @package Phrity > Util > ErrorHandler
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Phrity\Util;
|
||||||
|
|
||||||
|
use ErrorException;
|
||||||
|
use RuntimeException;
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ErrorHandler test class.
|
||||||
|
*/
|
||||||
|
class ErrorHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set up for all tests
|
||||||
|
*/
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
error_reporting(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetNull(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$handler->set();
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
try {
|
||||||
|
trigger_error('An error');
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$this->assertEquals('An error', $e->getMessage());
|
||||||
|
$this->assertEquals(0, $e->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $e->getSeverity());
|
||||||
|
$this->assertNull($e->getPrevious());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore handler
|
||||||
|
$this->assertTrue($handler->restore());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetThrowable(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$handler->set(new RuntimeException('A provided exception', 23));
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
try {
|
||||||
|
trigger_error('An error');
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
$this->assertEquals('A provided exception', $e->getMessage());
|
||||||
|
$this->assertEquals(23, $e->getCode());
|
||||||
|
$this->assertNotNull($e->getPrevious());
|
||||||
|
$prev = $e->getPrevious();
|
||||||
|
$this->assertEquals('An error', $prev->getMessage());
|
||||||
|
$this->assertEquals(0, $prev->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $prev->getSeverity());
|
||||||
|
$this->assertNull($prev->getPrevious());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore handler
|
||||||
|
$this->assertTrue($handler->restore());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetCallback(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$result = null;
|
||||||
|
$handler->set(function ($error) use (&$result) {
|
||||||
|
$result = [
|
||||||
|
'message' => $error->getMessage(),
|
||||||
|
'code' => $error->getCode(),
|
||||||
|
'severity' => $error->getSeverity(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
trigger_error('An error');
|
||||||
|
$this->assertEquals([
|
||||||
|
'message' => 'An error',
|
||||||
|
'code' => 0,
|
||||||
|
'severity' => E_USER_NOTICE,
|
||||||
|
], $result);
|
||||||
|
|
||||||
|
// Restore handler
|
||||||
|
$this->assertTrue($handler->restore());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithNull(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$check = false;
|
||||||
|
|
||||||
|
// No exception
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
$this->assertEquals('Code success', $result);
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
try {
|
||||||
|
$result = $handler->with(function () use (&$check) {
|
||||||
|
trigger_error('An error');
|
||||||
|
$check = true;
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$this->assertEquals('An error', $e->getMessage());
|
||||||
|
$this->assertEquals(0, $e->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $e->getSeverity());
|
||||||
|
$this->assertNull($e->getPrevious());
|
||||||
|
}
|
||||||
|
$this->assertFalse($check);
|
||||||
|
|
||||||
|
// Verify that exception is thrown
|
||||||
|
$this->expectException('ErrorException');
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
trigger_error('An error');
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithThrowable(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$check = false;
|
||||||
|
|
||||||
|
// No exception
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
$this->assertEquals('Code success', $result);
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
try {
|
||||||
|
$result = $handler->with(function () use (&$check) {
|
||||||
|
trigger_error('An error');
|
||||||
|
$check = true;
|
||||||
|
return 'Code success';
|
||||||
|
}, new RuntimeException('A provided exception', 23));
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
$this->assertEquals('A provided exception', $e->getMessage());
|
||||||
|
$this->assertEquals(23, $e->getCode());
|
||||||
|
$this->assertNotNull($e->getPrevious());
|
||||||
|
$prev = $e->getPrevious();
|
||||||
|
$this->assertEquals('An error', $prev->getMessage());
|
||||||
|
$this->assertEquals(0, $prev->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $prev->getSeverity());
|
||||||
|
$this->assertNull($prev->getPrevious());
|
||||||
|
}
|
||||||
|
$this->assertFalse($check);
|
||||||
|
|
||||||
|
// Verify that exception is thrown
|
||||||
|
$this->expectException('RuntimeException');
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
trigger_error('An error');
|
||||||
|
return 'Code success';
|
||||||
|
}, new RuntimeException('A provided exception', 23));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithCallback(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$check = false;
|
||||||
|
|
||||||
|
// No error invoked
|
||||||
|
$result = $handler->with(function () {
|
||||||
|
return 'Code success';
|
||||||
|
}, function ($error) {
|
||||||
|
return $error;
|
||||||
|
});
|
||||||
|
$this->assertEquals('Code success', $result);
|
||||||
|
|
||||||
|
// An error is invoked
|
||||||
|
$result = $handler->with(function () use (&$check) {
|
||||||
|
trigger_error('An error');
|
||||||
|
$check = true;
|
||||||
|
return 'Code success';
|
||||||
|
}, function ($error) {
|
||||||
|
return $error;
|
||||||
|
});
|
||||||
|
$this->assertFalse($check);
|
||||||
|
|
||||||
|
$this->assertEquals('An error', $result->getMessage());
|
||||||
|
$this->assertEquals(0, $result->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $result->getSeverity());
|
||||||
|
$this->assertNull($result->getPrevious());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithAllNull(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$check = false;
|
||||||
|
|
||||||
|
// No error invoked
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
$this->assertEquals('Code success', $result);
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
try {
|
||||||
|
$result = $handler->withAll(function () use (&$check) {
|
||||||
|
trigger_error('An error');
|
||||||
|
$check = true;
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$this->assertEquals('An error', $e->getMessage());
|
||||||
|
$this->assertEquals(0, $e->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $e->getSeverity());
|
||||||
|
$this->assertNull($e->getPrevious());
|
||||||
|
}
|
||||||
|
$this->assertTrue($check);
|
||||||
|
|
||||||
|
// Verify that exception is thrown
|
||||||
|
$this->expectException('ErrorException');
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
trigger_error('An error');
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithAllThrowable(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$check = false;
|
||||||
|
|
||||||
|
// No exception
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
return 'Code success';
|
||||||
|
});
|
||||||
|
$this->assertEquals('Code success', $result);
|
||||||
|
|
||||||
|
// Verify exception
|
||||||
|
try {
|
||||||
|
$result = $handler->withAll(function () use (&$check) {
|
||||||
|
trigger_error('An error');
|
||||||
|
$check = true;
|
||||||
|
return 'Code success';
|
||||||
|
}, new RuntimeException('A provided exception', 23));
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
$this->assertEquals('A provided exception', $e->getMessage());
|
||||||
|
$this->assertEquals(23, $e->getCode());
|
||||||
|
$this->assertNotNull($e->getPrevious());
|
||||||
|
$prev = $e->getPrevious();
|
||||||
|
$this->assertEquals('An error', $prev->getMessage());
|
||||||
|
$this->assertEquals(0, $prev->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $prev->getSeverity());
|
||||||
|
$this->assertNull($prev->getPrevious());
|
||||||
|
}
|
||||||
|
$this->assertTrue($check);
|
||||||
|
|
||||||
|
// Verify that exception is thrown
|
||||||
|
$this->expectException('RuntimeException');
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
trigger_error('An error');
|
||||||
|
return 'Code success';
|
||||||
|
}, new RuntimeException('A provided exception', 23));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithAllCallback(): void
|
||||||
|
{
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$check = false;
|
||||||
|
|
||||||
|
// No error invoked
|
||||||
|
$result = $handler->withAll(function () {
|
||||||
|
return 'Code success';
|
||||||
|
}, function ($error, $result) {
|
||||||
|
return $error;
|
||||||
|
});
|
||||||
|
$this->assertEquals('Code success', $result);
|
||||||
|
|
||||||
|
// An error is invoked
|
||||||
|
$result = $handler->withAll(function () use (&$check) {
|
||||||
|
trigger_error('An error');
|
||||||
|
trigger_error('Another error', E_USER_WARNING);
|
||||||
|
$check = true;
|
||||||
|
return 'Code success';
|
||||||
|
}, function ($errors, $result) {
|
||||||
|
return ['errors' => $errors, 'result' => $result];
|
||||||
|
});
|
||||||
|
$this->assertTrue($check);
|
||||||
|
|
||||||
|
$this->assertEquals('Code success', $result['result']);
|
||||||
|
$this->assertEquals('An error', $result['errors'][0]->getMessage());
|
||||||
|
$this->assertEquals(0, $result['errors'][0]->getCode());
|
||||||
|
$this->assertEquals(E_USER_NOTICE, $result['errors'][0]->getSeverity());
|
||||||
|
$this->assertNull($result['errors'][0]->getPrevious());
|
||||||
|
$this->assertEquals('Another error', $result['errors'][1]->getMessage());
|
||||||
|
$this->assertEquals(0, $result['errors'][1]->getCode());
|
||||||
|
$this->assertEquals(E_USER_WARNING, $result['errors'][1]->getSeverity());
|
||||||
|
$this->assertNull($result['errors'][1]->getPrevious());
|
||||||
|
}
|
||||||
|
}
|
21
vendor/psr/http-factory/LICENSE
vendored
Normal file
21
vendor/psr/http-factory/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 PHP-FIG
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
12
vendor/psr/http-factory/README.md
vendored
Normal file
12
vendor/psr/http-factory/README.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
HTTP Factories
|
||||||
|
==============
|
||||||
|
|
||||||
|
This repository holds all interfaces related to [PSR-17 (HTTP Factories)][psr-url].
|
||||||
|
|
||||||
|
Note that this is not a HTTP Factory implementation of its own. It is merely interfaces that describe the components of a HTTP Factory.
|
||||||
|
|
||||||
|
The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
|
||||||
|
|
||||||
|
[psr-url]: https://www.php-fig.org/psr/psr-17/
|
||||||
|
[package-url]: https://packagist.org/packages/psr/http-factory
|
||||||
|
[implementation-url]: https://packagist.org/providers/psr/http-factory-implementation
|
38
vendor/psr/http-factory/composer.json
vendored
Normal file
38
vendor/psr/http-factory/composer.json
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "psr/http-factory",
|
||||||
|
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||||
|
"keywords": [
|
||||||
|
"psr",
|
||||||
|
"psr-7",
|
||||||
|
"psr-17",
|
||||||
|
"http",
|
||||||
|
"factory",
|
||||||
|
"message",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/php-fig/http-factory"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.0.0",
|
||||||
|
"psr/http-message": "^1.0 || ^2.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
vendor/psr/http-factory/src/RequestFactoryInterface.php
vendored
Normal file
18
vendor/psr/http-factory/src/RequestFactoryInterface.php
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
interface RequestFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new request.
|
||||||
|
*
|
||||||
|
* @param string $method The HTTP method associated with the request.
|
||||||
|
* @param UriInterface|string $uri The URI associated with the request. If
|
||||||
|
* the value is a string, the factory MUST create a UriInterface
|
||||||
|
* instance based on it.
|
||||||
|
*
|
||||||
|
* @return RequestInterface
|
||||||
|
*/
|
||||||
|
public function createRequest(string $method, $uri): RequestInterface;
|
||||||
|
}
|
18
vendor/psr/http-factory/src/ResponseFactoryInterface.php
vendored
Normal file
18
vendor/psr/http-factory/src/ResponseFactoryInterface.php
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
interface ResponseFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new response.
|
||||||
|
*
|
||||||
|
* @param int $code HTTP status code; defaults to 200
|
||||||
|
* @param string $reasonPhrase Reason phrase to associate with status code
|
||||||
|
* in generated response; if none is provided implementations MAY use
|
||||||
|
* the defaults as suggested in the HTTP specification.
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
|
||||||
|
}
|
24
vendor/psr/http-factory/src/ServerRequestFactoryInterface.php
vendored
Normal file
24
vendor/psr/http-factory/src/ServerRequestFactoryInterface.php
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
interface ServerRequestFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new server request.
|
||||||
|
*
|
||||||
|
* Note that server-params are taken precisely as given - no parsing/processing
|
||||||
|
* of the given values is performed, and, in particular, no attempt is made to
|
||||||
|
* determine the HTTP method or URI, which must be provided explicitly.
|
||||||
|
*
|
||||||
|
* @param string $method The HTTP method associated with the request.
|
||||||
|
* @param UriInterface|string $uri The URI associated with the request. If
|
||||||
|
* the value is a string, the factory MUST create a UriInterface
|
||||||
|
* instance based on it.
|
||||||
|
* @param array $serverParams Array of SAPI parameters with which to seed
|
||||||
|
* the generated request instance.
|
||||||
|
*
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface;
|
||||||
|
}
|
45
vendor/psr/http-factory/src/StreamFactoryInterface.php
vendored
Normal file
45
vendor/psr/http-factory/src/StreamFactoryInterface.php
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
interface StreamFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new stream from a string.
|
||||||
|
*
|
||||||
|
* The stream SHOULD be created with a temporary resource.
|
||||||
|
*
|
||||||
|
* @param string $content String content with which to populate the stream.
|
||||||
|
*
|
||||||
|
* @return StreamInterface
|
||||||
|
*/
|
||||||
|
public function createStream(string $content = ''): StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a stream from an existing file.
|
||||||
|
*
|
||||||
|
* The file MUST be opened using the given mode, which may be any mode
|
||||||
|
* supported by the `fopen` function.
|
||||||
|
*
|
||||||
|
* The `$filename` MAY be any string supported by `fopen()`.
|
||||||
|
*
|
||||||
|
* @param string $filename Filename or stream URI to use as basis of stream.
|
||||||
|
* @param string $mode Mode with which to open the underlying filename/stream.
|
||||||
|
*
|
||||||
|
* @return StreamInterface
|
||||||
|
* @throws \RuntimeException If the file cannot be opened.
|
||||||
|
* @throws \InvalidArgumentException If the mode is invalid.
|
||||||
|
*/
|
||||||
|
public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new stream from an existing resource.
|
||||||
|
*
|
||||||
|
* The stream MUST be readable and may be writable.
|
||||||
|
*
|
||||||
|
* @param resource $resource PHP resource to use as basis of stream.
|
||||||
|
*
|
||||||
|
* @return StreamInterface
|
||||||
|
*/
|
||||||
|
public function createStreamFromResource($resource): StreamInterface;
|
||||||
|
}
|
34
vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
vendored
Normal file
34
vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
interface UploadedFileFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new uploaded file.
|
||||||
|
*
|
||||||
|
* If a size is not provided it will be determined by checking the size of
|
||||||
|
* the file.
|
||||||
|
*
|
||||||
|
* @see http://php.net/manual/features.file-upload.post-method.php
|
||||||
|
* @see http://php.net/manual/features.file-upload.errors.php
|
||||||
|
*
|
||||||
|
* @param StreamInterface $stream Underlying stream representing the
|
||||||
|
* uploaded file content.
|
||||||
|
* @param int $size in bytes
|
||||||
|
* @param int $error PHP file upload error
|
||||||
|
* @param string $clientFilename Filename as provided by the client, if any.
|
||||||
|
* @param string $clientMediaType Media type as provided by the client, if any.
|
||||||
|
*
|
||||||
|
* @return UploadedFileInterface
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the file resource is not readable.
|
||||||
|
*/
|
||||||
|
public function createUploadedFile(
|
||||||
|
StreamInterface $stream,
|
||||||
|
int $size = null,
|
||||||
|
int $error = \UPLOAD_ERR_OK,
|
||||||
|
string $clientFilename = null,
|
||||||
|
string $clientMediaType = null
|
||||||
|
): UploadedFileInterface;
|
||||||
|
}
|
17
vendor/psr/http-factory/src/UriFactoryInterface.php
vendored
Normal file
17
vendor/psr/http-factory/src/UriFactoryInterface.php
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
interface UriFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new URI.
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
*
|
||||||
|
* @return UriInterface
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException If the given URI cannot be parsed.
|
||||||
|
*/
|
||||||
|
public function createUri(string $uri = ''): UriInterface;
|
||||||
|
}
|
36
vendor/psr/http-message/CHANGELOG.md
vendored
Normal file
36
vendor/psr/http-message/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file, in reverse chronological order by release.
|
||||||
|
|
||||||
|
## 1.0.1 - 2016-08-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Nothing.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- Nothing.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Nothing.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Updated all `@return self` annotation references in interfaces to use
|
||||||
|
`@return static`, which more closelly follows the semantics of the
|
||||||
|
specification.
|
||||||
|
- Updated the `MessageInterface::getHeaders()` return annotation to use the
|
||||||
|
value `string[][]`, indicating the format is a nested array of strings.
|
||||||
|
- Updated the `@link` annotation for `RequestInterface::withRequestTarget()`
|
||||||
|
to point to the correct section of RFC 7230.
|
||||||
|
- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation
|
||||||
|
to add the parameter name (`$uploadedFiles`).
|
||||||
|
- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()`
|
||||||
|
method to correctly reference the method parameter (it was referencing an
|
||||||
|
incorrect parameter name previously).
|
||||||
|
|
||||||
|
## 1.0.0 - 2016-05-18
|
||||||
|
|
||||||
|
Initial stable release; reflects accepted PSR-7 specification.
|
19
vendor/psr/http-message/LICENSE
vendored
Normal file
19
vendor/psr/http-message/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2014 PHP Framework Interoperability Group
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
16
vendor/psr/http-message/README.md
vendored
Normal file
16
vendor/psr/http-message/README.md
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
PSR Http Message
|
||||||
|
================
|
||||||
|
|
||||||
|
This repository holds all interfaces/classes/traits related to
|
||||||
|
[PSR-7](http://www.php-fig.org/psr/psr-7/).
|
||||||
|
|
||||||
|
Note that this is not a HTTP message implementation of its own. It is merely an
|
||||||
|
interface that describes a HTTP message. See the specification for more details.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Before reading the usage guide we recommend reading the PSR-7 interfaces method list:
|
||||||
|
|
||||||
|
* [`PSR-7 Interfaces Method List`](docs/PSR7-Interfaces.md)
|
||||||
|
* [`PSR-7 Usage Guide`](docs/PSR7-Usage.md)
|
26
vendor/psr/http-message/composer.json
vendored
Normal file
26
vendor/psr/http-message/composer.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "psr/http-message",
|
||||||
|
"description": "Common interface for HTTP messages",
|
||||||
|
"keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
|
||||||
|
"homepage": "https://github.com/php-fig/http-message",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.1.x-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
130
vendor/psr/http-message/docs/PSR7-Interfaces.md
vendored
Normal file
130
vendor/psr/http-message/docs/PSR7-Interfaces.md
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# Interfaces
|
||||||
|
|
||||||
|
The purpose of this list is to help in finding the methods when working with PSR-7. This can be considered as a cheatsheet for PSR-7 interfaces.
|
||||||
|
|
||||||
|
The interfaces defined in PSR-7 are the following:
|
||||||
|
|
||||||
|
| Class Name | Description |
|
||||||
|
|---|---|
|
||||||
|
| [Psr\Http\Message\MessageInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagemessageinterface) | Representation of a HTTP message |
|
||||||
|
| [Psr\Http\Message\RequestInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagerequestinterface) | Representation of an outgoing, client-side request. |
|
||||||
|
| [Psr\Http\Message\ServerRequestInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageserverrequestinterface) | Representation of an incoming, server-side HTTP request. |
|
||||||
|
| [Psr\Http\Message\ResponseInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageresponseinterface) | Representation of an outgoing, server-side response. |
|
||||||
|
| [Psr\Http\Message\StreamInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagestreaminterface) | Describes a data stream |
|
||||||
|
| [Psr\Http\Message\UriInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageuriinterface) | Value object representing a URI. |
|
||||||
|
| [Psr\Http\Message\UploadedFileInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageuploadedfileinterface) | Value object representing a file uploaded through an HTTP request. |
|
||||||
|
|
||||||
|
## `Psr\Http\Message\MessageInterface` Methods
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `getProtocolVersion()` | Retrieve HTTP protocol version | 1.0 or 1.1 |
|
||||||
|
| `withProtocolVersion($version)` | Returns new message instance with given HTTP protocol version | |
|
||||||
|
| `getHeaders()` | Retrieve all HTTP Headers | [Request Header List](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields), [Response Header List](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields) |
|
||||||
|
| `hasHeader($name)` | Checks if HTTP Header with given name exists | |
|
||||||
|
| `getHeader($name)` | Retrieves a array with the values for a single header | |
|
||||||
|
| `getHeaderLine($name)` | Retrieves a comma-separated string of the values for a single header | |
|
||||||
|
| `withHeader($name, $value)` | Returns new message instance with given HTTP Header | if the header existed in the original instance, replaces the header value from the original message with the value provided when creating the new instance. |
|
||||||
|
| `withAddedHeader($name, $value)` | Returns new message instance with appended value to given header | If header already exists value will be appended, if not a new header will be created |
|
||||||
|
| `withoutHeader($name)` | Removes HTTP Header with given name| |
|
||||||
|
| `getBody()` | Retrieves the HTTP Message Body | Returns object implementing `StreamInterface`|
|
||||||
|
| `withBody(StreamInterface $body)` | Returns new message instance with given HTTP Message Body | |
|
||||||
|
|
||||||
|
|
||||||
|
## `Psr\Http\Message\RequestInterface` Methods
|
||||||
|
|
||||||
|
Same methods as `Psr\Http\Message\MessageInterface` + the following methods:
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `getRequestTarget()` | Retrieves the message's request target | origin-form, absolute-form, authority-form, asterisk-form ([RFC7230](https://www.rfc-editor.org/rfc/rfc7230.txt)) |
|
||||||
|
| `withRequestTarget($requestTarget)` | Return a new message instance with the specific request-target | |
|
||||||
|
| `getMethod()` | Retrieves the HTTP method of the request. | GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE (defined in [RFC7231](https://tools.ietf.org/html/rfc7231)), PATCH (defined in [RFC5789](https://tools.ietf.org/html/rfc5789)) |
|
||||||
|
| `withMethod($method)` | Returns a new message instance with the provided HTTP method | |
|
||||||
|
| `getUri()` | Retrieves the URI instance | |
|
||||||
|
| `withUri(UriInterface $uri, $preserveHost = false)` | Returns a new message instance with the provided URI | |
|
||||||
|
|
||||||
|
|
||||||
|
## `Psr\Http\Message\ServerRequestInterface` Methods
|
||||||
|
|
||||||
|
Same methods as `Psr\Http\Message\RequestInterface` + the following methods:
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `getServerParams() ` | Retrieve server parameters | Typically derived from `$_SERVER` |
|
||||||
|
| `getCookieParams()` | Retrieves cookies sent by the client to the server. | Typically derived from `$_COOKIES` |
|
||||||
|
| `withCookieParams(array $cookies)` | Returns a new request instance with the specified cookies | |
|
||||||
|
| `withQueryParams(array $query)` | Returns a new request instance with the specified query string arguments | |
|
||||||
|
| `getUploadedFiles()` | Retrieve normalized file upload data | |
|
||||||
|
| `withUploadedFiles(array $uploadedFiles)` | Returns a new request instance with the specified uploaded files | |
|
||||||
|
| `getParsedBody()` | Retrieve any parameters provided in the request body | |
|
||||||
|
| `withParsedBody($data)` | Returns a new request instance with the specified body parameters | |
|
||||||
|
| `getAttributes()` | Retrieve attributes derived from the request | |
|
||||||
|
| `getAttribute($name, $default = null)` | Retrieve a single derived request attribute | |
|
||||||
|
| `withAttribute($name, $value)` | Returns a new request instance with the specified derived request attribute | |
|
||||||
|
| `withoutAttribute($name)` | Returns a new request instance that without the specified derived request attribute | |
|
||||||
|
|
||||||
|
## `Psr\Http\Message\ResponseInterface` Methods:
|
||||||
|
|
||||||
|
Same methods as `Psr\Http\Message\MessageInterface` + the following methods:
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `getStatusCode()` | Gets the response status code. | |
|
||||||
|
| `withStatus($code, $reasonPhrase = '')` | Returns a new response instance with the specified status code and, optionally, reason phrase. | |
|
||||||
|
| `getReasonPhrase()` | Gets the response reason phrase associated with the status code. | |
|
||||||
|
|
||||||
|
## `Psr\Http\Message\StreamInterface` Methods
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `__toString()` | Reads all data from the stream into a string, from the beginning to end. | |
|
||||||
|
| `close()` | Closes the stream and any underlying resources. | |
|
||||||
|
| `detach()` | Separates any underlying resources from the stream. | |
|
||||||
|
| `getSize()` | Get the size of the stream if known. | |
|
||||||
|
| `eof()` | Returns true if the stream is at the end of the stream.| |
|
||||||
|
| `isSeekable()` | Returns whether or not the stream is seekable. | |
|
||||||
|
| `seek($offset, $whence = SEEK_SET)` | Seek to a position in the stream. | |
|
||||||
|
| `rewind()` | Seek to the beginning of the stream. | |
|
||||||
|
| `isWritable()` | Returns whether or not the stream is writable. | |
|
||||||
|
| `write($string)` | Write data to the stream. | |
|
||||||
|
| `isReadable()` | Returns whether or not the stream is readable. | |
|
||||||
|
| `read($length)` | Read data from the stream. | |
|
||||||
|
| `getContents()` | Returns the remaining contents in a string | |
|
||||||
|
| `getMetadata($key = null)()` | Get stream metadata as an associative array or retrieve a specific key. | |
|
||||||
|
|
||||||
|
## `Psr\Http\Message\UriInterface` Methods
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `getScheme()` | Retrieve the scheme component of the URI. | |
|
||||||
|
| `getAuthority()` | Retrieve the authority component of the URI. | |
|
||||||
|
| `getUserInfo()` | Retrieve the user information component of the URI. | |
|
||||||
|
| `getHost()` | Retrieve the host component of the URI. | |
|
||||||
|
| `getPort()` | Retrieve the port component of the URI. | |
|
||||||
|
| `getPath()` | Retrieve the path component of the URI. | |
|
||||||
|
| `getQuery()` | Retrieve the query string of the URI. | |
|
||||||
|
| `getFragment()` | Retrieve the fragment component of the URI. | |
|
||||||
|
| `withScheme($scheme)` | Return an instance with the specified scheme. | |
|
||||||
|
| `withUserInfo($user, $password = null)` | Return an instance with the specified user information. | |
|
||||||
|
| `withHost($host)` | Return an instance with the specified host. | |
|
||||||
|
| `withPort($port)` | Return an instance with the specified port. | |
|
||||||
|
| `withPath($path)` | Return an instance with the specified path. | |
|
||||||
|
| `withQuery($query)` | Return an instance with the specified query string. | |
|
||||||
|
| `withFragment($fragment)` | Return an instance with the specified URI fragment. | |
|
||||||
|
| `__toString()` | Return the string representation as a URI reference. | |
|
||||||
|
|
||||||
|
## `Psr\Http\Message\UploadedFileInterface` Methods
|
||||||
|
|
||||||
|
| Method Name | Description | Notes |
|
||||||
|
|------------------------------------| ----------- | ----- |
|
||||||
|
| `getStream()` | Retrieve a stream representing the uploaded file. | |
|
||||||
|
| `moveTo($targetPath)` | Move the uploaded file to a new location. | |
|
||||||
|
| `getSize()` | Retrieve the file size. | |
|
||||||
|
| `getError()` | Retrieve the error associated with the uploaded file. | |
|
||||||
|
| `getClientFilename()` | Retrieve the filename sent by the client. | |
|
||||||
|
| `getClientMediaType()` | Retrieve the media type sent by the client. | |
|
||||||
|
|
||||||
|
> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`.
|
||||||
|
> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered.
|
||||||
|
|
159
vendor/psr/http-message/docs/PSR7-Usage.md
vendored
Normal file
159
vendor/psr/http-message/docs/PSR7-Usage.md
vendored
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
### PSR-7 Usage
|
||||||
|
|
||||||
|
All PSR-7 applications comply with these interfaces
|
||||||
|
They were created to establish a standard between middleware implementations.
|
||||||
|
|
||||||
|
> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`.
|
||||||
|
> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered.
|
||||||
|
|
||||||
|
|
||||||
|
The following examples will illustrate how basic operations are done in PSR-7.
|
||||||
|
|
||||||
|
##### Examples
|
||||||
|
|
||||||
|
|
||||||
|
For this examples to work (at least) a PSR-7 implementation package is required. (eg: zendframework/zend-diactoros, guzzlehttp/psr7, slim/slim, etc)
|
||||||
|
All PSR-7 implementations should have the same behaviour.
|
||||||
|
|
||||||
|
The following will be assumed:
|
||||||
|
`$request` is an object of `Psr\Http\Message\RequestInterface` and
|
||||||
|
|
||||||
|
`$response` is an object implementing `Psr\Http\Message\RequestInterface`
|
||||||
|
|
||||||
|
|
||||||
|
### Working with HTTP Headers
|
||||||
|
|
||||||
|
#### Adding headers to response:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$response->withHeader('My-Custom-Header', 'My Custom Message');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Appending values to headers
|
||||||
|
|
||||||
|
```php
|
||||||
|
$response->withAddedHeader('My-Custom-Header', 'The second message');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Checking if header exists:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$request->hasHeader('My-Custom-Header'); // will return false
|
||||||
|
$response->hasHeader('My-Custom-Header'); // will return true
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: My-Custom-Header was only added in the Response
|
||||||
|
|
||||||
|
#### Getting comma-separated values from a header (also applies to request)
|
||||||
|
|
||||||
|
```php
|
||||||
|
// getting value from request headers
|
||||||
|
$request->getHeaderLine('Content-Type'); // will return: "text/html; charset=UTF-8"
|
||||||
|
// getting value from response headers
|
||||||
|
$response->getHeaderLine('My-Custom-Header'); // will return: "My Custom Message; The second message"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Getting array of value from a header (also applies to request)
|
||||||
|
```php
|
||||||
|
// getting value from request headers
|
||||||
|
$request->getHeader('Content-Type'); // will return: ["text/html", "charset=UTF-8"]
|
||||||
|
// getting value from response headers
|
||||||
|
$response->getHeader('My-Custom-Header'); // will return: ["My Custom Message", "The second message"]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Removing headers from HTTP Messages
|
||||||
|
```php
|
||||||
|
// removing a header from Request, removing deprecated "Content-MD5" header
|
||||||
|
$request->withoutHeader('Content-MD5');
|
||||||
|
|
||||||
|
// removing a header from Response
|
||||||
|
// effect: the browser won't know the size of the stream
|
||||||
|
// the browser will download the stream till it ends
|
||||||
|
$response->withoutHeader('Content-Length');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with HTTP Message Body
|
||||||
|
|
||||||
|
When working with the PSR-7 there are two methods of implementation:
|
||||||
|
#### 1. Getting the body separately
|
||||||
|
|
||||||
|
> This method makes the body handling easier to understand and is useful when repeatedly calling body methods. (You only call `getBody()` once). Using this method mistakes like `$response->write()` are also prevented.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$body = $response->getBody();
|
||||||
|
// operations on body, eg. read, write, seek
|
||||||
|
// ...
|
||||||
|
// replacing the old body
|
||||||
|
$response->withBody($body);
|
||||||
|
// this last statement is optional as we working with objects
|
||||||
|
// in this case the "new" body is same with the "old" one
|
||||||
|
// the $body variable has the same value as the one in $request, only the reference is passed
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Working directly on response
|
||||||
|
|
||||||
|
> This method is useful when only performing few operations as the `$request->getBody()` statement fragment is required
|
||||||
|
|
||||||
|
```php
|
||||||
|
$response->getBody()->write('hello');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting the body contents
|
||||||
|
|
||||||
|
The following snippet gets the contents of a stream contents.
|
||||||
|
> Note: Streams must be rewinded, if content was written into streams, it will be ignored when calling `getContents()` because the stream pointer is set to the last character, which is `\0` - meaning end of stream.
|
||||||
|
```php
|
||||||
|
$body = $response->getBody();
|
||||||
|
$body->rewind(); // or $body->seek(0);
|
||||||
|
$bodyText = $body->getContents();
|
||||||
|
```
|
||||||
|
> Note: If `$body->seek(1)` is called before `$body->getContents()`, the first character will be ommited as the starting pointer is set to `1`, not `0`. This is why using `$body->rewind()` is recommended.
|
||||||
|
|
||||||
|
### Append to body
|
||||||
|
|
||||||
|
```php
|
||||||
|
$response->getBody()->write('Hello'); // writing directly
|
||||||
|
$body = $request->getBody(); // which is a `StreamInterface`
|
||||||
|
$body->write('xxxxx');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prepend to body
|
||||||
|
Prepending is different when it comes to streams. The content must be copied before writing the content to be prepended.
|
||||||
|
The following example will explain the behaviour of streams.
|
||||||
|
|
||||||
|
```php
|
||||||
|
// assuming our response is initially empty
|
||||||
|
$body = $repsonse->getBody();
|
||||||
|
// writing the string "abcd"
|
||||||
|
$body->write('abcd');
|
||||||
|
|
||||||
|
// seeking to start of stream
|
||||||
|
$body->seek(0);
|
||||||
|
// writing 'ef'
|
||||||
|
$body->write('ef'); // at this point the stream contains "efcd"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Prepending by rewriting separately
|
||||||
|
|
||||||
|
```php
|
||||||
|
// assuming our response body stream only contains: "abcd"
|
||||||
|
$body = $response->getBody();
|
||||||
|
$body->rewind();
|
||||||
|
$contents = $body->getContents(); // abcd
|
||||||
|
// seeking the stream to beginning
|
||||||
|
$body->rewind();
|
||||||
|
$body->write('ef'); // stream contains "efcd"
|
||||||
|
$body->write($contents); // stream contains "efabcd"
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: `getContents()` seeks the stream while reading it, therefore if the second `rewind()` method call was not present the stream would have resulted in `abcdefabcd` because the `write()` method appends to stream if not preceeded by `rewind()` or `seek(0)`.
|
||||||
|
|
||||||
|
#### Prepending by using contents as a string
|
||||||
|
```php
|
||||||
|
$body = $response->getBody();
|
||||||
|
$body->rewind();
|
||||||
|
$contents = $body->getContents(); // efabcd
|
||||||
|
$contents = 'ef'.$contents;
|
||||||
|
$body->rewind();
|
||||||
|
$body->write($contents);
|
||||||
|
```
|
189
vendor/psr/http-message/src/MessageInterface.php
vendored
Normal file
189
vendor/psr/http-message/src/MessageInterface.php
vendored
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP messages consist of requests from a client to a server and responses
|
||||||
|
* from a server to a client. This interface defines the methods common to
|
||||||
|
* each.
|
||||||
|
*
|
||||||
|
* Messages are considered immutable; all methods that might change state MUST
|
||||||
|
* be implemented such that they retain the internal state of the current
|
||||||
|
* message and return an instance that contains the changed state.
|
||||||
|
*
|
||||||
|
* @link http://www.ietf.org/rfc/rfc7230.txt
|
||||||
|
* @link http://www.ietf.org/rfc/rfc7231.txt
|
||||||
|
*/
|
||||||
|
interface MessageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves the HTTP protocol version as a string.
|
||||||
|
*
|
||||||
|
* The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
|
||||||
|
*
|
||||||
|
* @return string HTTP protocol version.
|
||||||
|
*/
|
||||||
|
public function getProtocolVersion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified HTTP protocol version.
|
||||||
|
*
|
||||||
|
* The version string MUST contain only the HTTP version number (e.g.,
|
||||||
|
* "1.1", "1.0").
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* new protocol version.
|
||||||
|
*
|
||||||
|
* @param string $version HTTP protocol version
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withProtocolVersion(string $version);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all message header values.
|
||||||
|
*
|
||||||
|
* The keys represent the header name as it will be sent over the wire, and
|
||||||
|
* each value is an array of strings associated with the header.
|
||||||
|
*
|
||||||
|
* // Represent the headers as a string
|
||||||
|
* foreach ($message->getHeaders() as $name => $values) {
|
||||||
|
* echo $name . ": " . implode(", ", $values);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Emit headers iteratively:
|
||||||
|
* foreach ($message->getHeaders() as $name => $values) {
|
||||||
|
* foreach ($values as $value) {
|
||||||
|
* header(sprintf('%s: %s', $name, $value), false);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* While header names are not case-sensitive, getHeaders() will preserve the
|
||||||
|
* exact case in which headers were originally specified.
|
||||||
|
*
|
||||||
|
* @return string[][] Returns an associative array of the message's headers. Each
|
||||||
|
* key MUST be a header name, and each value MUST be an array of strings
|
||||||
|
* for that header.
|
||||||
|
*/
|
||||||
|
public function getHeaders();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a header exists by the given case-insensitive name.
|
||||||
|
*
|
||||||
|
* @param string $name Case-insensitive header field name.
|
||||||
|
* @return bool Returns true if any header names match the given header
|
||||||
|
* name using a case-insensitive string comparison. Returns false if
|
||||||
|
* no matching header name is found in the message.
|
||||||
|
*/
|
||||||
|
public function hasHeader(string $name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a message header value by the given case-insensitive name.
|
||||||
|
*
|
||||||
|
* This method returns an array of all the header values of the given
|
||||||
|
* case-insensitive header name.
|
||||||
|
*
|
||||||
|
* If the header does not appear in the message, this method MUST return an
|
||||||
|
* empty array.
|
||||||
|
*
|
||||||
|
* @param string $name Case-insensitive header field name.
|
||||||
|
* @return string[] An array of string values as provided for the given
|
||||||
|
* header. If the header does not appear in the message, this method MUST
|
||||||
|
* return an empty array.
|
||||||
|
*/
|
||||||
|
public function getHeader(string $name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a comma-separated string of the values for a single header.
|
||||||
|
*
|
||||||
|
* This method returns all of the header values of the given
|
||||||
|
* case-insensitive header name as a string concatenated together using
|
||||||
|
* a comma.
|
||||||
|
*
|
||||||
|
* NOTE: Not all header values may be appropriately represented using
|
||||||
|
* comma concatenation. For such headers, use getHeader() instead
|
||||||
|
* and supply your own delimiter when concatenating.
|
||||||
|
*
|
||||||
|
* If the header does not appear in the message, this method MUST return
|
||||||
|
* an empty string.
|
||||||
|
*
|
||||||
|
* @param string $name Case-insensitive header field name.
|
||||||
|
* @return string A string of values as provided for the given header
|
||||||
|
* concatenated together using a comma. If the header does not appear in
|
||||||
|
* the message, this method MUST return an empty string.
|
||||||
|
*/
|
||||||
|
public function getHeaderLine(string $name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the provided value replacing the specified header.
|
||||||
|
*
|
||||||
|
* While header names are case-insensitive, the casing of the header will
|
||||||
|
* be preserved by this function, and returned from getHeaders().
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* new and/or updated header and value.
|
||||||
|
*
|
||||||
|
* @param string $name Case-insensitive header field name.
|
||||||
|
* @param string|string[] $value Header value(s).
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException for invalid header names or values.
|
||||||
|
*/
|
||||||
|
public function withHeader(string $name, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified header appended with the given value.
|
||||||
|
*
|
||||||
|
* Existing values for the specified header will be maintained. The new
|
||||||
|
* value(s) will be appended to the existing list. If the header did not
|
||||||
|
* exist previously, it will be added.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* new header and/or value.
|
||||||
|
*
|
||||||
|
* @param string $name Case-insensitive header field name to add.
|
||||||
|
* @param string|string[] $value Header value(s).
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException for invalid header names or values.
|
||||||
|
*/
|
||||||
|
public function withAddedHeader(string $name, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance without the specified header.
|
||||||
|
*
|
||||||
|
* Header resolution MUST be done without case-sensitivity.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that removes
|
||||||
|
* the named header.
|
||||||
|
*
|
||||||
|
* @param string $name Case-insensitive header field name to remove.
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withoutHeader(string $name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the body of the message.
|
||||||
|
*
|
||||||
|
* @return StreamInterface Returns the body as a stream.
|
||||||
|
*/
|
||||||
|
public function getBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified message body.
|
||||||
|
*
|
||||||
|
* The body MUST be a StreamInterface object.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return a new instance that has the
|
||||||
|
* new body stream.
|
||||||
|
*
|
||||||
|
* @param StreamInterface $body Body.
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException When the body is not valid.
|
||||||
|
*/
|
||||||
|
public function withBody(StreamInterface $body);
|
||||||
|
}
|
131
vendor/psr/http-message/src/RequestInterface.php
vendored
Normal file
131
vendor/psr/http-message/src/RequestInterface.php
vendored
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of an outgoing, client-side request.
|
||||||
|
*
|
||||||
|
* Per the HTTP specification, this interface includes properties for
|
||||||
|
* each of the following:
|
||||||
|
*
|
||||||
|
* - Protocol version
|
||||||
|
* - HTTP method
|
||||||
|
* - URI
|
||||||
|
* - Headers
|
||||||
|
* - Message body
|
||||||
|
*
|
||||||
|
* During construction, implementations MUST attempt to set the Host header from
|
||||||
|
* a provided URI if no Host header is provided.
|
||||||
|
*
|
||||||
|
* Requests are considered immutable; all methods that might change state MUST
|
||||||
|
* be implemented such that they retain the internal state of the current
|
||||||
|
* message and return an instance that contains the changed state.
|
||||||
|
*/
|
||||||
|
interface RequestInterface extends MessageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves the message's request target.
|
||||||
|
*
|
||||||
|
* Retrieves the message's request-target either as it will appear (for
|
||||||
|
* clients), as it appeared at request (for servers), or as it was
|
||||||
|
* specified for the instance (see withRequestTarget()).
|
||||||
|
*
|
||||||
|
* In most cases, this will be the origin-form of the composed URI,
|
||||||
|
* unless a value was provided to the concrete implementation (see
|
||||||
|
* withRequestTarget() below).
|
||||||
|
*
|
||||||
|
* If no URI is available, and no request-target has been specifically
|
||||||
|
* provided, this method MUST return the string "/".
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRequestTarget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specific request-target.
|
||||||
|
*
|
||||||
|
* If the request needs a non-origin-form request-target — e.g., for
|
||||||
|
* specifying an absolute-form, authority-form, or asterisk-form —
|
||||||
|
* this method may be used to create an instance with the specified
|
||||||
|
* request-target, verbatim.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* changed request target.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
|
||||||
|
* request-target forms allowed in request messages)
|
||||||
|
* @param string $requestTarget
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withRequestTarget(string $requestTarget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the HTTP method of the request.
|
||||||
|
*
|
||||||
|
* @return string Returns the request method.
|
||||||
|
*/
|
||||||
|
public function getMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the provided HTTP method.
|
||||||
|
*
|
||||||
|
* While HTTP method names are typically all uppercase characters, HTTP
|
||||||
|
* method names are case-sensitive and thus implementations SHOULD NOT
|
||||||
|
* modify the given string.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* changed request method.
|
||||||
|
*
|
||||||
|
* @param string $method Case-sensitive method.
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException for invalid HTTP methods.
|
||||||
|
*/
|
||||||
|
public function withMethod(string $method);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the URI instance.
|
||||||
|
*
|
||||||
|
* This method MUST return a UriInterface instance.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc3986#section-4.3
|
||||||
|
* @return UriInterface Returns a UriInterface instance
|
||||||
|
* representing the URI of the request.
|
||||||
|
*/
|
||||||
|
public function getUri();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance with the provided URI.
|
||||||
|
*
|
||||||
|
* This method MUST update the Host header of the returned request by
|
||||||
|
* default if the URI contains a host component. If the URI does not
|
||||||
|
* contain a host component, any pre-existing Host header MUST be carried
|
||||||
|
* over to the returned request.
|
||||||
|
*
|
||||||
|
* You can opt-in to preserving the original state of the Host header by
|
||||||
|
* setting `$preserveHost` to `true`. When `$preserveHost` is set to
|
||||||
|
* `true`, this method interacts with the Host header in the following ways:
|
||||||
|
*
|
||||||
|
* - If the Host header is missing or empty, and the new URI contains
|
||||||
|
* a host component, this method MUST update the Host header in the returned
|
||||||
|
* request.
|
||||||
|
* - If the Host header is missing or empty, and the new URI does not contain a
|
||||||
|
* host component, this method MUST NOT update the Host header in the returned
|
||||||
|
* request.
|
||||||
|
* - If a Host header is present and non-empty, this method MUST NOT update
|
||||||
|
* the Host header in the returned request.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* new UriInterface instance.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc3986#section-4.3
|
||||||
|
* @param UriInterface $uri New request URI to use.
|
||||||
|
* @param bool $preserveHost Preserve the original state of the Host header.
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withUri(UriInterface $uri, bool $preserveHost = false);
|
||||||
|
}
|
70
vendor/psr/http-message/src/ResponseInterface.php
vendored
Normal file
70
vendor/psr/http-message/src/ResponseInterface.php
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of an outgoing, server-side response.
|
||||||
|
*
|
||||||
|
* Per the HTTP specification, this interface includes properties for
|
||||||
|
* each of the following:
|
||||||
|
*
|
||||||
|
* - Protocol version
|
||||||
|
* - Status code and reason phrase
|
||||||
|
* - Headers
|
||||||
|
* - Message body
|
||||||
|
*
|
||||||
|
* Responses are considered immutable; all methods that might change state MUST
|
||||||
|
* be implemented such that they retain the internal state of the current
|
||||||
|
* message and return an instance that contains the changed state.
|
||||||
|
*/
|
||||||
|
interface ResponseInterface extends MessageInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Gets the response status code.
|
||||||
|
*
|
||||||
|
* The status code is a 3-digit integer result code of the server's attempt
|
||||||
|
* to understand and satisfy the request.
|
||||||
|
*
|
||||||
|
* @return int Status code.
|
||||||
|
*/
|
||||||
|
public function getStatusCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified status code and, optionally, reason phrase.
|
||||||
|
*
|
||||||
|
* If no reason phrase is specified, implementations MAY choose to default
|
||||||
|
* to the RFC 7231 or IANA recommended reason phrase for the response's
|
||||||
|
* status code.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* updated status and reason phrase.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc7231#section-6
|
||||||
|
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||||
|
* @param int $code The 3-digit integer result code to set.
|
||||||
|
* @param string $reasonPhrase The reason phrase to use with the
|
||||||
|
* provided status code; if none is provided, implementations MAY
|
||||||
|
* use the defaults as suggested in the HTTP specification.
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException For invalid status code arguments.
|
||||||
|
*/
|
||||||
|
public function withStatus(int $code, string $reasonPhrase = '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the response reason phrase associated with the status code.
|
||||||
|
*
|
||||||
|
* Because a reason phrase is not a required element in a response
|
||||||
|
* status line, the reason phrase value MAY be null. Implementations MAY
|
||||||
|
* choose to return the default RFC 7231 recommended reason phrase (or those
|
||||||
|
* listed in the IANA HTTP Status Code Registry) for the response's
|
||||||
|
* status code.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc7231#section-6
|
||||||
|
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||||
|
* @return string Reason phrase; must return an empty string if none present.
|
||||||
|
*/
|
||||||
|
public function getReasonPhrase();
|
||||||
|
}
|
263
vendor/psr/http-message/src/ServerRequestInterface.php
vendored
Normal file
263
vendor/psr/http-message/src/ServerRequestInterface.php
vendored
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of an incoming, server-side HTTP request.
|
||||||
|
*
|
||||||
|
* Per the HTTP specification, this interface includes properties for
|
||||||
|
* each of the following:
|
||||||
|
*
|
||||||
|
* - Protocol version
|
||||||
|
* - HTTP method
|
||||||
|
* - URI
|
||||||
|
* - Headers
|
||||||
|
* - Message body
|
||||||
|
*
|
||||||
|
* Additionally, it encapsulates all data as it has arrived to the
|
||||||
|
* application from the CGI and/or PHP environment, including:
|
||||||
|
*
|
||||||
|
* - The values represented in $_SERVER.
|
||||||
|
* - Any cookies provided (generally via $_COOKIE)
|
||||||
|
* - Query string arguments (generally via $_GET, or as parsed via parse_str())
|
||||||
|
* - Upload files, if any (as represented by $_FILES)
|
||||||
|
* - Deserialized body parameters (generally from $_POST)
|
||||||
|
*
|
||||||
|
* $_SERVER values MUST be treated as immutable, as they represent application
|
||||||
|
* state at the time of request; as such, no methods are provided to allow
|
||||||
|
* modification of those values. The other values provide such methods, as they
|
||||||
|
* can be restored from $_SERVER or the request body, and may need treatment
|
||||||
|
* during the application (e.g., body parameters may be deserialized based on
|
||||||
|
* content type).
|
||||||
|
*
|
||||||
|
* Additionally, this interface recognizes the utility of introspecting a
|
||||||
|
* request to derive and match additional parameters (e.g., via URI path
|
||||||
|
* matching, decrypting cookie values, deserializing non-form-encoded body
|
||||||
|
* content, matching authorization headers to users, etc). These parameters
|
||||||
|
* are stored in an "attributes" property.
|
||||||
|
*
|
||||||
|
* Requests are considered immutable; all methods that might change state MUST
|
||||||
|
* be implemented such that they retain the internal state of the current
|
||||||
|
* message and return an instance that contains the changed state.
|
||||||
|
*/
|
||||||
|
interface ServerRequestInterface extends RequestInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve server parameters.
|
||||||
|
*
|
||||||
|
* Retrieves data related to the incoming request environment,
|
||||||
|
* typically derived from PHP's $_SERVER superglobal. The data IS NOT
|
||||||
|
* REQUIRED to originate from $_SERVER.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getServerParams();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve cookies.
|
||||||
|
*
|
||||||
|
* Retrieves cookies sent by the client to the server.
|
||||||
|
*
|
||||||
|
* The data MUST be compatible with the structure of the $_COOKIE
|
||||||
|
* superglobal.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCookieParams();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified cookies.
|
||||||
|
*
|
||||||
|
* The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
|
||||||
|
* be compatible with the structure of $_COOKIE. Typically, this data will
|
||||||
|
* be injected at instantiation.
|
||||||
|
*
|
||||||
|
* This method MUST NOT update the related Cookie header of the request
|
||||||
|
* instance, nor related values in the server params.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* updated cookie values.
|
||||||
|
*
|
||||||
|
* @param array $cookies Array of key/value pairs representing cookies.
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withCookieParams(array $cookies);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve query string arguments.
|
||||||
|
*
|
||||||
|
* Retrieves the deserialized query string arguments, if any.
|
||||||
|
*
|
||||||
|
* Note: the query params might not be in sync with the URI or server
|
||||||
|
* params. If you need to ensure you are only getting the original
|
||||||
|
* values, you may need to parse the query string from `getUri()->getQuery()`
|
||||||
|
* or from the `QUERY_STRING` server param.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getQueryParams();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified query string arguments.
|
||||||
|
*
|
||||||
|
* These values SHOULD remain immutable over the course of the incoming
|
||||||
|
* request. They MAY be injected during instantiation, such as from PHP's
|
||||||
|
* $_GET superglobal, or MAY be derived from some other value such as the
|
||||||
|
* URI. In cases where the arguments are parsed from the URI, the data
|
||||||
|
* MUST be compatible with what PHP's parse_str() would return for
|
||||||
|
* purposes of how duplicate query parameters are handled, and how nested
|
||||||
|
* sets are handled.
|
||||||
|
*
|
||||||
|
* Setting query string arguments MUST NOT change the URI stored by the
|
||||||
|
* request, nor the values in the server params.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* updated query string arguments.
|
||||||
|
*
|
||||||
|
* @param array $query Array of query string arguments, typically from
|
||||||
|
* $_GET.
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withQueryParams(array $query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve normalized file upload data.
|
||||||
|
*
|
||||||
|
* This method returns upload metadata in a normalized tree, with each leaf
|
||||||
|
* an instance of Psr\Http\Message\UploadedFileInterface.
|
||||||
|
*
|
||||||
|
* These values MAY be prepared from $_FILES or the message body during
|
||||||
|
* instantiation, or MAY be injected via withUploadedFiles().
|
||||||
|
*
|
||||||
|
* @return array An array tree of UploadedFileInterface instances; an empty
|
||||||
|
* array MUST be returned if no data is present.
|
||||||
|
*/
|
||||||
|
public function getUploadedFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with the specified uploaded files.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* updated body parameters.
|
||||||
|
*
|
||||||
|
* @param array $uploadedFiles An array tree of UploadedFileInterface instances.
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException if an invalid structure is provided.
|
||||||
|
*/
|
||||||
|
public function withUploadedFiles(array $uploadedFiles);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve any parameters provided in the request body.
|
||||||
|
*
|
||||||
|
* If the request Content-Type is either application/x-www-form-urlencoded
|
||||||
|
* or multipart/form-data, and the request method is POST, this method MUST
|
||||||
|
* return the contents of $_POST.
|
||||||
|
*
|
||||||
|
* Otherwise, this method may return any results of deserializing
|
||||||
|
* the request body content; as parsing returns structured content, the
|
||||||
|
* potential types MUST be arrays or objects only. A null value indicates
|
||||||
|
* the absence of body content.
|
||||||
|
*
|
||||||
|
* @return null|array|object The deserialized body parameters, if any.
|
||||||
|
* These will typically be an array or object.
|
||||||
|
*/
|
||||||
|
public function getParsedBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified body parameters.
|
||||||
|
*
|
||||||
|
* These MAY be injected during instantiation.
|
||||||
|
*
|
||||||
|
* If the request Content-Type is either application/x-www-form-urlencoded
|
||||||
|
* or multipart/form-data, and the request method is POST, use this method
|
||||||
|
* ONLY to inject the contents of $_POST.
|
||||||
|
*
|
||||||
|
* The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
|
||||||
|
* deserializing the request body content. Deserialization/parsing returns
|
||||||
|
* structured data, and, as such, this method ONLY accepts arrays or objects,
|
||||||
|
* or a null value if nothing was available to parse.
|
||||||
|
*
|
||||||
|
* As an example, if content negotiation determines that the request data
|
||||||
|
* is a JSON payload, this method could be used to create a request
|
||||||
|
* instance with the deserialized parameters.
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* updated body parameters.
|
||||||
|
*
|
||||||
|
* @param null|array|object $data The deserialized body data. This will
|
||||||
|
* typically be in an array or object.
|
||||||
|
* @return static
|
||||||
|
* @throws \InvalidArgumentException if an unsupported argument type is
|
||||||
|
* provided.
|
||||||
|
*/
|
||||||
|
public function withParsedBody($data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve attributes derived from the request.
|
||||||
|
*
|
||||||
|
* The request "attributes" may be used to allow injection of any
|
||||||
|
* parameters derived from the request: e.g., the results of path
|
||||||
|
* match operations; the results of decrypting cookies; the results of
|
||||||
|
* deserializing non-form-encoded message bodies; etc. Attributes
|
||||||
|
* will be application and request specific, and CAN be mutable.
|
||||||
|
*
|
||||||
|
* @return array Attributes derived from the request.
|
||||||
|
*/
|
||||||
|
public function getAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a single derived request attribute.
|
||||||
|
*
|
||||||
|
* Retrieves a single derived request attribute as described in
|
||||||
|
* getAttributes(). If the attribute has not been previously set, returns
|
||||||
|
* the default value as provided.
|
||||||
|
*
|
||||||
|
* This method obviates the need for a hasAttribute() method, as it allows
|
||||||
|
* specifying a default value to return if the attribute is not found.
|
||||||
|
*
|
||||||
|
* @see getAttributes()
|
||||||
|
* @param string $name The attribute name.
|
||||||
|
* @param mixed $default Default value to return if the attribute does not exist.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAttribute(string $name, $default = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified derived request attribute.
|
||||||
|
*
|
||||||
|
* This method allows setting a single derived request attribute as
|
||||||
|
* described in getAttributes().
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that has the
|
||||||
|
* updated attribute.
|
||||||
|
*
|
||||||
|
* @see getAttributes()
|
||||||
|
* @param string $name The attribute name.
|
||||||
|
* @param mixed $value The value of the attribute.
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withAttribute(string $name, $value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance that removes the specified derived request attribute.
|
||||||
|
*
|
||||||
|
* This method allows removing a single derived request attribute as
|
||||||
|
* described in getAttributes().
|
||||||
|
*
|
||||||
|
* This method MUST be implemented in such a way as to retain the
|
||||||
|
* immutability of the message, and MUST return an instance that removes
|
||||||
|
* the attribute.
|
||||||
|
*
|
||||||
|
* @see getAttributes()
|
||||||
|
* @param string $name The attribute name.
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function withoutAttribute(string $name);
|
||||||
|
}
|
160
vendor/psr/http-message/src/StreamInterface.php
vendored
Normal file
160
vendor/psr/http-message/src/StreamInterface.php
vendored
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a data stream.
|
||||||
|
*
|
||||||
|
* Typically, an instance will wrap a PHP stream; this interface provides
|
||||||
|
* a wrapper around the most common operations, including serialization of
|
||||||
|
* the entire stream to a string.
|
||||||
|
*/
|
||||||
|
interface StreamInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Reads all data from the stream into a string, from the beginning to end.
|
||||||
|
*
|
||||||
|
* This method MUST attempt to seek to the beginning of the stream before
|
||||||
|
* reading data and read the stream until the end is reached.
|
||||||
|
*
|
||||||
|
* Warning: This could attempt to load a large amount of data into memory.
|
||||||
|
*
|
||||||
|
* This method MUST NOT raise an exception in order to conform with PHP's
|
||||||
|
* string casting operations.
|
||||||
|
*
|
||||||
|
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the stream and any underlying resources.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Separates any underlying resources from the stream.
|
||||||
|
*
|
||||||
|
* After the stream has been detached, the stream is in an unusable state.
|
||||||
|
*
|
||||||
|
* @return resource|null Underlying PHP stream, if any
|
||||||
|
*/
|
||||||
|
public function detach();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the stream if known.
|
||||||
|
*
|
||||||
|
* @return int|null Returns the size in bytes if known, or null if unknown.
|
||||||
|
*/
|
||||||
|
public function getSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current position of the file read/write pointer
|
||||||
|
*
|
||||||
|
* @return int Position of the file pointer
|
||||||
|
* @throws \RuntimeException on error.
|
||||||
|
*/
|
||||||
|
public function tell();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the stream is at the end of the stream.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function eof();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the stream is seekable.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isSeekable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek to a position in the stream.
|
||||||
|
*
|
||||||
|
* @link http://www.php.net/manual/en/function.fseek.php
|
||||||
|
* @param int $offset Stream offset
|
||||||
|
* @param int $whence Specifies how the cursor position will be calculated
|
||||||
|
* based on the seek offset. Valid values are identical to the built-in
|
||||||
|
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
|
||||||
|
* offset bytes SEEK_CUR: Set position to current location plus offset
|
||||||
|
* SEEK_END: Set position to end-of-stream plus offset.
|
||||||
|
* @throws \RuntimeException on failure.
|
||||||
|
*/
|
||||||
|
public function seek(int $offset, int $whence = SEEK_SET);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek to the beginning of the stream.
|
||||||
|
*
|
||||||
|
* If the stream is not seekable, this method will raise an exception;
|
||||||
|
* otherwise, it will perform a seek(0).
|
||||||
|
*
|
||||||
|
* @see seek()
|
||||||
|
* @link http://www.php.net/manual/en/function.fseek.php
|
||||||
|
* @throws \RuntimeException on failure.
|
||||||
|
*/
|
||||||
|
public function rewind();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the stream is writable.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isWritable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the stream.
|
||||||
|
*
|
||||||
|
* @param string $string The string that is to be written.
|
||||||
|
* @return int Returns the number of bytes written to the stream.
|
||||||
|
* @throws \RuntimeException on failure.
|
||||||
|
*/
|
||||||
|
public function write(string $string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the stream is readable.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isReadable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the stream.
|
||||||
|
*
|
||||||
|
* @param int $length Read up to $length bytes from the object and return
|
||||||
|
* them. Fewer than $length bytes may be returned if underlying stream
|
||||||
|
* call returns fewer bytes.
|
||||||
|
* @return string Returns the data read from the stream, or an empty string
|
||||||
|
* if no bytes are available.
|
||||||
|
* @throws \RuntimeException if an error occurs.
|
||||||
|
*/
|
||||||
|
public function read(int $length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remaining contents in a string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \RuntimeException if unable to read or an error occurs while
|
||||||
|
* reading.
|
||||||
|
*/
|
||||||
|
public function getContents();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get stream metadata as an associative array or retrieve a specific key.
|
||||||
|
*
|
||||||
|
* The keys returned are identical to the keys returned from PHP's
|
||||||
|
* stream_get_meta_data() function.
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/function.stream-get-meta-data.php
|
||||||
|
* @param string|null $key Specific metadata to retrieve.
|
||||||
|
* @return array|mixed|null Returns an associative array if no key is
|
||||||
|
* provided. Returns a specific key value if a key is provided and the
|
||||||
|
* value is found, or null if the key is not found.
|
||||||
|
*/
|
||||||
|
public function getMetadata(?string $key = null);
|
||||||
|
}
|
125
vendor/psr/http-message/src/UploadedFileInterface.php
vendored
Normal file
125
vendor/psr/http-message/src/UploadedFileInterface.php
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object representing a file uploaded through an HTTP request.
|
||||||
|
*
|
||||||
|
* Instances of this interface are considered immutable; all methods that
|
||||||
|
* might change state MUST be implemented such that they retain the internal
|
||||||
|
* state of the current instance and return an instance that contains the
|
||||||
|
* changed state.
|
||||||
|
*/
|
||||||
|
interface UploadedFileInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve a stream representing the uploaded file.
|
||||||
|
*
|
||||||
|
* This method MUST return a StreamInterface instance, representing the
|
||||||
|
* uploaded file. The purpose of this method is to allow utilizing native PHP
|
||||||
|
* stream functionality to manipulate the file upload, such as
|
||||||
|
* stream_copy_to_stream() (though the result will need to be decorated in a
|
||||||
|
* native PHP stream wrapper to work with such functions).
|
||||||
|
*
|
||||||
|
* If the moveTo() method has been called previously, this method MUST raise
|
||||||
|
* an exception.
|
||||||
|
*
|
||||||
|
* @return StreamInterface Stream representation of the uploaded file.
|
||||||
|
* @throws \RuntimeException in cases when no stream is available or can be
|
||||||
|
* created.
|
||||||
|
*/
|
||||||
|
public function getStream();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the uploaded file to a new location.
|
||||||
|
*
|
||||||
|
* Use this method as an alternative to move_uploaded_file(). This method is
|
||||||
|
* guaranteed to work in both SAPI and non-SAPI environments.
|
||||||
|
* Implementations must determine which environment they are in, and use the
|
||||||
|
* appropriate method (move_uploaded_file(), rename(), or a stream
|
||||||
|
* operation) to perform the operation.
|
||||||
|
*
|
||||||
|
* $targetPath may be an absolute path, or a relative path. If it is a
|
||||||
|
* relative path, resolution should be the same as used by PHP's rename()
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* The original file or stream MUST be removed on completion.
|
||||||
|
*
|
||||||
|
* If this method is called more than once, any subsequent calls MUST raise
|
||||||
|
* an exception.
|
||||||
|
*
|
||||||
|
* When used in an SAPI environment where $_FILES is populated, when writing
|
||||||
|
* files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
|
||||||
|
* used to ensure permissions and upload status are verified correctly.
|
||||||
|
*
|
||||||
|
* If you wish to move to a stream, use getStream(), as SAPI operations
|
||||||
|
* cannot guarantee writing to stream destinations.
|
||||||
|
*
|
||||||
|
* @see http://php.net/is_uploaded_file
|
||||||
|
* @see http://php.net/move_uploaded_file
|
||||||
|
* @param string $targetPath Path to which to move the uploaded file.
|
||||||
|
* @throws \InvalidArgumentException if the $targetPath specified is invalid.
|
||||||
|
* @throws \RuntimeException on any error during the move operation, or on
|
||||||
|
* the second or subsequent call to the method.
|
||||||
|
*/
|
||||||
|
public function moveTo(string $targetPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the file size.
|
||||||
|
*
|
||||||
|
* Implementations SHOULD return the value stored in the "size" key of
|
||||||
|
* the file in the $_FILES array if available, as PHP calculates this based
|
||||||
|
* on the actual size transmitted.
|
||||||
|
*
|
||||||
|
* @return int|null The file size in bytes or null if unknown.
|
||||||
|
*/
|
||||||
|
public function getSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the error associated with the uploaded file.
|
||||||
|
*
|
||||||
|
* The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
|
||||||
|
*
|
||||||
|
* If the file was uploaded successfully, this method MUST return
|
||||||
|
* UPLOAD_ERR_OK.
|
||||||
|
*
|
||||||
|
* Implementations SHOULD return the value stored in the "error" key of
|
||||||
|
* the file in the $_FILES array.
|
||||||
|
*
|
||||||
|
* @see http://php.net/manual/en/features.file-upload.errors.php
|
||||||
|
* @return int One of PHP's UPLOAD_ERR_XXX constants.
|
||||||
|
*/
|
||||||
|
public function getError();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the filename sent by the client.
|
||||||
|
*
|
||||||
|
* Do not trust the value returned by this method. A client could send
|
||||||
|
* a malicious filename with the intention to corrupt or hack your
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
* Implementations SHOULD return the value stored in the "name" key of
|
||||||
|
* the file in the $_FILES array.
|
||||||
|
*
|
||||||
|
* @return string|null The filename sent by the client or null if none
|
||||||
|
* was provided.
|
||||||
|
*/
|
||||||
|
public function getClientFilename();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the media type sent by the client.
|
||||||
|
*
|
||||||
|
* Do not trust the value returned by this method. A client could send
|
||||||
|
* a malicious media type with the intention to corrupt or hack your
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
* Implementations SHOULD return the value stored in the "type" key of
|
||||||
|
* the file in the $_FILES array.
|
||||||
|
*
|
||||||
|
* @return string|null The media type sent by the client or null if none
|
||||||
|
* was provided.
|
||||||
|
*/
|
||||||
|
public function getClientMediaType();
|
||||||
|
}
|
326
vendor/psr/http-message/src/UriInterface.php
vendored
Normal file
326
vendor/psr/http-message/src/UriInterface.php
vendored
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psr\Http\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value object representing a URI.
|
||||||
|
*
|
||||||
|
* This interface is meant to represent URIs according to RFC 3986 and to
|
||||||
|
* provide methods for most common operations. Additional functionality for
|
||||||
|
* working with URIs can be provided on top of the interface or externally.
|
||||||
|
* Its primary use is for HTTP requests, but may also be used in other
|
||||||
|
* contexts.
|
||||||
|
*
|
||||||
|
* Instances of this interface are considered immutable; all methods that
|
||||||
|
* might change state MUST be implemented such that they retain the internal
|
||||||
|
* state of the current instance and return an instance that contains the
|
||||||
|
* changed state.
|
||||||
|
*
|
||||||
|
* Typically the Host header will be also be present in the request message.
|
||||||
|
* For server-side requests, the scheme will typically be discoverable in the
|
||||||
|
* server parameters.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc3986 (the URI specification)
|
||||||
|
*/
|
||||||
|
interface UriInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve the scheme component of the URI.
|
||||||
|
*
|
||||||
|
* If no scheme is present, this method MUST return an empty string.
|
||||||
|
*
|
||||||
|
* The value returned MUST be normalized to lowercase, per RFC 3986
|
||||||
|
* Section 3.1.
|
||||||
|
*
|
||||||
|
* The trailing ":" character is not part of the scheme and MUST NOT be
|
||||||
|
* added.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-3.1
|
||||||
|
* @return string The URI scheme.
|
||||||
|
*/
|
||||||
|
public function getScheme();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the authority component of the URI.
|
||||||
|
*
|
||||||
|
* If no authority information is present, this method MUST return an empty
|
||||||
|
* string.
|
||||||
|
*
|
||||||
|
* The authority syntax of the URI is:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* [user-info@]host[:port]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* If the port component is not set or is the standard port for the current
|
||||||
|
* scheme, it SHOULD NOT be included.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-3.2
|
||||||
|
* @return string The URI authority, in "[user-info@]host[:port]" format.
|
||||||
|
*/
|
||||||
|
public function getAuthority();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the user information component of the URI.
|
||||||
|
*
|
||||||
|
* If no user information is present, this method MUST return an empty
|
||||||
|
* string.
|
||||||
|
*
|
||||||
|
* If a user is present in the URI, this will return that value;
|
||||||
|
* additionally, if the password is also present, it will be appended to the
|
||||||
|
* user value, with a colon (":") separating the values.
|
||||||
|
*
|
||||||
|
* The trailing "@" character is not part of the user information and MUST
|
||||||
|
* NOT be added.
|
||||||
|
*
|
||||||
|
* @return string The URI user information, in "username[:password]" format.
|
||||||
|
*/
|
||||||
|
public function getUserInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the host component of the URI.
|
||||||
|
*
|
||||||
|
* If no host is present, this method MUST return an empty string.
|
||||||
|
*
|
||||||
|
* The value returned MUST be normalized to lowercase, per RFC 3986
|
||||||
|
* Section 3.2.2.
|
||||||
|
*
|
||||||
|
* @see http://tools.ietf.org/html/rfc3986#section-3.2.2
|
||||||
|
* @return string The URI host.
|
||||||
|
*/
|
||||||
|
public function getHost();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the port component of the URI.
|
||||||
|
*
|
||||||
|
* If a port is present, and it is non-standard for the current scheme,
|
||||||
|
* this method MUST return it as an integer. If the port is the standard port
|
||||||
|
* used with the current scheme, this method SHOULD return null.
|
||||||
|
*
|
||||||
|
* If no port is present, and no scheme is present, this method MUST return
|
||||||
|
* a null value.
|
||||||
|
*
|
||||||
|
* If no port is present, but a scheme is present, this method MAY return
|
||||||
|
* the standard port for that scheme, but SHOULD return null.
|
||||||
|
*
|
||||||
|
* @return null|int The URI port.
|
||||||
|
*/
|
||||||
|
public function getPort();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the path component of the URI.
|
||||||
|
*
|
||||||
|
* The path can either be empty or absolute (starting with a slash) or
|
||||||
|
* rootless (not starting with a slash). Implementations MUST support all
|
||||||
|
* three syntaxes.
|
||||||
|
*
|
||||||
|
* Normally, the empty path "" and absolute path "/" are considered equal as
|
||||||
|
* defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
|
||||||
|
* do this normalization because in contexts with a trimmed base path, e.g.
|
||||||
|
* the front controller, this difference becomes significant. It's the task
|
||||||
|
* of the user to handle both "" and "/".
|
||||||
|
*
|
||||||
|
* The value returned MUST be percent-encoded, but MUST NOT double-encode
|
||||||
|
* any characters. To determine what characters to encode, please refer to
|
||||||
|
* RFC 3986, Sections 2 and 3.3.
|
||||||
|
*
|
||||||
|
* As an example, if the value should include a slash ("/") not intended as
|
||||||
|
* delimiter between path segments, that value MUST be passed in encoded
|
||||||
|
* form (e.g., "%2F") to the instance.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-2
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-3.3
|
||||||
|
* @return string The URI path.
|
||||||
|
*/
|
||||||
|
public function getPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the query string of the URI.
|
||||||
|
*
|
||||||
|
* If no query string is present, this method MUST return an empty string.
|
||||||
|
*
|
||||||
|
* The leading "?" character is not part of the query and MUST NOT be
|
||||||
|
* added.
|
||||||
|
*
|
||||||
|
* The value returned MUST be percent-encoded, but MUST NOT double-encode
|
||||||
|
* any characters. To determine what characters to encode, please refer to
|
||||||
|
* RFC 3986, Sections 2 and 3.4.
|
||||||
|
*
|
||||||
|
* As an example, if a value in a key/value pair of the query string should
|
||||||
|
* include an ampersand ("&") not intended as a delimiter between values,
|
||||||
|
* that value MUST be passed in encoded form (e.g., "%26") to the instance.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-2
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-3.4
|
||||||
|
* @return string The URI query string.
|
||||||
|
*/
|
||||||
|
public function getQuery();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the fragment component of the URI.
|
||||||
|
*
|
||||||
|
* If no fragment is present, this method MUST return an empty string.
|
||||||
|
*
|
||||||
|
* The leading "#" character is not part of the fragment and MUST NOT be
|
||||||
|
* added.
|
||||||
|
*
|
||||||
|
* The value returned MUST be percent-encoded, but MUST NOT double-encode
|
||||||
|
* any characters. To determine what characters to encode, please refer to
|
||||||
|
* RFC 3986, Sections 2 and 3.5.
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-2
|
||||||
|
* @see https://tools.ietf.org/html/rfc3986#section-3.5
|
||||||
|
* @return string The URI fragment.
|
||||||
|
*/
|
||||||
|
public function getFragment();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified scheme.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified scheme.
|
||||||
|
*
|
||||||
|
* Implementations MUST support the schemes "http" and "https" case
|
||||||
|
* insensitively, and MAY accommodate other schemes if required.
|
||||||
|
*
|
||||||
|
* An empty scheme is equivalent to removing the scheme.
|
||||||
|
*
|
||||||
|
* @param string $scheme The scheme to use with the new instance.
|
||||||
|
* @return static A new instance with the specified scheme.
|
||||||
|
* @throws \InvalidArgumentException for invalid or unsupported schemes.
|
||||||
|
*/
|
||||||
|
public function withScheme(string $scheme);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified user information.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified user information.
|
||||||
|
*
|
||||||
|
* Password is optional, but the user information MUST include the
|
||||||
|
* user; an empty string for the user is equivalent to removing user
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* @param string $user The user name to use for authority.
|
||||||
|
* @param null|string $password The password associated with $user.
|
||||||
|
* @return static A new instance with the specified user information.
|
||||||
|
*/
|
||||||
|
public function withUserInfo(string $user, ?string $password = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified host.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified host.
|
||||||
|
*
|
||||||
|
* An empty host value is equivalent to removing the host.
|
||||||
|
*
|
||||||
|
* @param string $host The hostname to use with the new instance.
|
||||||
|
* @return static A new instance with the specified host.
|
||||||
|
* @throws \InvalidArgumentException for invalid hostnames.
|
||||||
|
*/
|
||||||
|
public function withHost(string $host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified port.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified port.
|
||||||
|
*
|
||||||
|
* Implementations MUST raise an exception for ports outside the
|
||||||
|
* established TCP and UDP port ranges.
|
||||||
|
*
|
||||||
|
* A null value provided for the port is equivalent to removing the port
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* @param null|int $port The port to use with the new instance; a null value
|
||||||
|
* removes the port information.
|
||||||
|
* @return static A new instance with the specified port.
|
||||||
|
* @throws \InvalidArgumentException for invalid ports.
|
||||||
|
*/
|
||||||
|
public function withPort(?int $port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified path.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified path.
|
||||||
|
*
|
||||||
|
* The path can either be empty or absolute (starting with a slash) or
|
||||||
|
* rootless (not starting with a slash). Implementations MUST support all
|
||||||
|
* three syntaxes.
|
||||||
|
*
|
||||||
|
* If the path is intended to be domain-relative rather than path relative then
|
||||||
|
* it must begin with a slash ("/"). Paths not starting with a slash ("/")
|
||||||
|
* are assumed to be relative to some base path known to the application or
|
||||||
|
* consumer.
|
||||||
|
*
|
||||||
|
* Users can provide both encoded and decoded path characters.
|
||||||
|
* Implementations ensure the correct encoding as outlined in getPath().
|
||||||
|
*
|
||||||
|
* @param string $path The path to use with the new instance.
|
||||||
|
* @return static A new instance with the specified path.
|
||||||
|
* @throws \InvalidArgumentException for invalid paths.
|
||||||
|
*/
|
||||||
|
public function withPath(string $path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified query string.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified query string.
|
||||||
|
*
|
||||||
|
* Users can provide both encoded and decoded query characters.
|
||||||
|
* Implementations ensure the correct encoding as outlined in getQuery().
|
||||||
|
*
|
||||||
|
* An empty query string value is equivalent to removing the query string.
|
||||||
|
*
|
||||||
|
* @param string $query The query string to use with the new instance.
|
||||||
|
* @return static A new instance with the specified query string.
|
||||||
|
* @throws \InvalidArgumentException for invalid query strings.
|
||||||
|
*/
|
||||||
|
public function withQuery(string $query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an instance with the specified URI fragment.
|
||||||
|
*
|
||||||
|
* This method MUST retain the state of the current instance, and return
|
||||||
|
* an instance that contains the specified URI fragment.
|
||||||
|
*
|
||||||
|
* Users can provide both encoded and decoded fragment characters.
|
||||||
|
* Implementations ensure the correct encoding as outlined in getFragment().
|
||||||
|
*
|
||||||
|
* An empty fragment value is equivalent to removing the fragment.
|
||||||
|
*
|
||||||
|
* @param string $fragment The fragment to use with the new instance.
|
||||||
|
* @return static A new instance with the specified fragment.
|
||||||
|
*/
|
||||||
|
public function withFragment(string $fragment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the string representation as a URI reference.
|
||||||
|
*
|
||||||
|
* Depending on which components of the URI are present, the resulting
|
||||||
|
* string is either a full URI or relative reference according to RFC 3986,
|
||||||
|
* Section 4.1. The method concatenates the various components of the URI,
|
||||||
|
* using the appropriate delimiters:
|
||||||
|
*
|
||||||
|
* - If a scheme is present, it MUST be suffixed by ":".
|
||||||
|
* - If an authority is present, it MUST be prefixed by "//".
|
||||||
|
* - The path can be concatenated without delimiters. But there are two
|
||||||
|
* cases where the path has to be adjusted to make the URI reference
|
||||||
|
* valid as PHP does not allow to throw an exception in __toString():
|
||||||
|
* - If the path is rootless and an authority is present, the path MUST
|
||||||
|
* be prefixed by "/".
|
||||||
|
* - If the path is starting with more than one "/" and no authority is
|
||||||
|
* present, the starting slashes MUST be reduced to one.
|
||||||
|
* - If a query is present, it MUST be prefixed by "?".
|
||||||
|
* - If a fragment is present, it MUST be prefixed by "#".
|
||||||
|
*
|
||||||
|
* @see http://tools.ietf.org/html/rfc3986#section-4.1
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString();
|
||||||
|
}
|
19
vendor/psr/log/LICENSE
vendored
Normal file
19
vendor/psr/log/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2012 PHP Framework Interoperability Group
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
58
vendor/psr/log/README.md
vendored
Normal file
58
vendor/psr/log/README.md
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
PSR Log
|
||||||
|
=======
|
||||||
|
|
||||||
|
This repository holds all interfaces/classes/traits related to
|
||||||
|
[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
|
||||||
|
|
||||||
|
Note that this is not a logger of its own. It is merely an interface that
|
||||||
|
describes a logger. See the specification for more details.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer require psr/log
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
If you need a logger, you can use the interface like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
public function __construct(LoggerInterface $logger = null)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doSomething()
|
||||||
|
{
|
||||||
|
if ($this->logger) {
|
||||||
|
$this->logger->info('Doing work');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->doSomethingElse();
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
$this->logger->error('Oh no!', array('exception' => $exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
// do something useful
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then pick one of the implementations of the interface to get a logger.
|
||||||
|
|
||||||
|
If you want to implement the interface, you can require this package and
|
||||||
|
implement `Psr\Log\LoggerInterface` in your code. Please read the
|
||||||
|
[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
|
||||||
|
for details.
|
26
vendor/psr/log/composer.json
vendored
Normal file
26
vendor/psr/log/composer.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"keywords": ["psr", "psr-3", "log"],
|
||||||
|
"homepage": "https://github.com/php-fig/log",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Log\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.x-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
vendor/psr/log/src/AbstractLogger.php
vendored
Normal file
15
vendor/psr/log/src/AbstractLogger.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple Logger implementation that other Loggers can inherit from.
|
||||||
|
*
|
||||||
|
* It simply delegates all log-level-specific methods to the `log` method to
|
||||||
|
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||||
|
* messages regardless of the error level has to implement.
|
||||||
|
*/
|
||||||
|
abstract class AbstractLogger implements LoggerInterface
|
||||||
|
{
|
||||||
|
use LoggerTrait;
|
||||||
|
}
|
7
vendor/psr/log/src/InvalidArgumentException.php
vendored
Normal file
7
vendor/psr/log/src/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
class InvalidArgumentException extends \InvalidArgumentException
|
||||||
|
{
|
||||||
|
}
|
18
vendor/psr/log/src/LogLevel.php
vendored
Normal file
18
vendor/psr/log/src/LogLevel.php
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes log levels.
|
||||||
|
*/
|
||||||
|
class LogLevel
|
||||||
|
{
|
||||||
|
const EMERGENCY = 'emergency';
|
||||||
|
const ALERT = 'alert';
|
||||||
|
const CRITICAL = 'critical';
|
||||||
|
const ERROR = 'error';
|
||||||
|
const WARNING = 'warning';
|
||||||
|
const NOTICE = 'notice';
|
||||||
|
const INFO = 'info';
|
||||||
|
const DEBUG = 'debug';
|
||||||
|
}
|
14
vendor/psr/log/src/LoggerAwareInterface.php
vendored
Normal file
14
vendor/psr/log/src/LoggerAwareInterface.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a logger-aware instance.
|
||||||
|
*/
|
||||||
|
interface LoggerAwareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sets a logger instance on the object.
|
||||||
|
*/
|
||||||
|
public function setLogger(LoggerInterface $logger): void;
|
||||||
|
}
|
22
vendor/psr/log/src/LoggerAwareTrait.php
vendored
Normal file
22
vendor/psr/log/src/LoggerAwareTrait.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic Implementation of LoggerAwareInterface.
|
||||||
|
*/
|
||||||
|
trait LoggerAwareTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The logger instance.
|
||||||
|
*/
|
||||||
|
protected ?LoggerInterface $logger = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a logger.
|
||||||
|
*/
|
||||||
|
public function setLogger(LoggerInterface $logger): void
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
}
|
98
vendor/psr/log/src/LoggerInterface.php
vendored
Normal file
98
vendor/psr/log/src/LoggerInterface.php
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a logger instance.
|
||||||
|
*
|
||||||
|
* The message MUST be a string or object implementing __toString().
|
||||||
|
*
|
||||||
|
* The message MAY contain placeholders in the form: {foo} where foo
|
||||||
|
* will be replaced by the context data in key "foo".
|
||||||
|
*
|
||||||
|
* The context array can contain arbitrary data. The only assumption that
|
||||||
|
* can be made by implementors is that if an Exception instance is given
|
||||||
|
* to produce a stack trace, it MUST be in a key named "exception".
|
||||||
|
*
|
||||||
|
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
|
||||||
|
* for the full interface specification.
|
||||||
|
*/
|
||||||
|
interface LoggerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* System is unusable.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function emergency(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action must be taken immediately.
|
||||||
|
*
|
||||||
|
* Example: Entire website down, database unavailable, etc. This should
|
||||||
|
* trigger the SMS alerts and wake you up.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function alert(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Critical conditions.
|
||||||
|
*
|
||||||
|
* Example: Application component unavailable, unexpected exception.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function critical(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime errors that do not require immediate action but should typically
|
||||||
|
* be logged and monitored.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function error(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exceptional occurrences that are not errors.
|
||||||
|
*
|
||||||
|
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||||
|
* that are not necessarily wrong.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function warning(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal but significant events.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function notice(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interesting events.
|
||||||
|
*
|
||||||
|
* Example: User logs in, SQL logs.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function info(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed debug information.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function debug(string|\Stringable $message, array $context = []): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs with an arbitrary level.
|
||||||
|
*
|
||||||
|
* @param mixed $level
|
||||||
|
* @param mixed[] $context
|
||||||
|
*
|
||||||
|
* @throws \Psr\Log\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function log($level, string|\Stringable $message, array $context = []): void;
|
||||||
|
}
|
98
vendor/psr/log/src/LoggerTrait.php
vendored
Normal file
98
vendor/psr/log/src/LoggerTrait.php
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple Logger trait that classes unable to extend AbstractLogger
|
||||||
|
* (because they extend another class, etc) can include.
|
||||||
|
*
|
||||||
|
* It simply delegates all log-level-specific methods to the `log` method to
|
||||||
|
* reduce boilerplate code that a simple Logger that does the same thing with
|
||||||
|
* messages regardless of the error level has to implement.
|
||||||
|
*/
|
||||||
|
trait LoggerTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* System is unusable.
|
||||||
|
*/
|
||||||
|
public function emergency(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::EMERGENCY, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action must be taken immediately.
|
||||||
|
*
|
||||||
|
* Example: Entire website down, database unavailable, etc. This should
|
||||||
|
* trigger the SMS alerts and wake you up.
|
||||||
|
*/
|
||||||
|
public function alert(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::ALERT, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Critical conditions.
|
||||||
|
*
|
||||||
|
* Example: Application component unavailable, unexpected exception.
|
||||||
|
*/
|
||||||
|
public function critical(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::CRITICAL, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime errors that do not require immediate action but should typically
|
||||||
|
* be logged and monitored.
|
||||||
|
*/
|
||||||
|
public function error(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::ERROR, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exceptional occurrences that are not errors.
|
||||||
|
*
|
||||||
|
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||||
|
* that are not necessarily wrong.
|
||||||
|
*/
|
||||||
|
public function warning(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::WARNING, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal but significant events.
|
||||||
|
*/
|
||||||
|
public function notice(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::NOTICE, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interesting events.
|
||||||
|
*
|
||||||
|
* Example: User logs in, SQL logs.
|
||||||
|
*/
|
||||||
|
public function info(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::INFO, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed debug information.
|
||||||
|
*/
|
||||||
|
public function debug(string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$this->log(LogLevel::DEBUG, $message, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs with an arbitrary level.
|
||||||
|
*
|
||||||
|
* @param mixed $level
|
||||||
|
*
|
||||||
|
* @throws \Psr\Log\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
abstract public function log($level, string|\Stringable $message, array $context = []): void;
|
||||||
|
}
|
26
vendor/psr/log/src/NullLogger.php
vendored
Normal file
26
vendor/psr/log/src/NullLogger.php
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Logger can be used to avoid conditional log calls.
|
||||||
|
*
|
||||||
|
* Logging should always be optional, and if no logger is provided to your
|
||||||
|
* library creating a NullLogger instance to have something to throw logs at
|
||||||
|
* is a good way to avoid littering your code with `if ($this->logger) { }`
|
||||||
|
* blocks.
|
||||||
|
*/
|
||||||
|
class NullLogger extends AbstractLogger
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Logs with an arbitrary level.
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*
|
||||||
|
* @throws \Psr\Log\InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function log($level, string|\Stringable $message, array $context = []): void
|
||||||
|
{
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
21
vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
21
vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Use this if you believe there is a bug in this repo
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
Please provide a clear and concise description of the suspected issue.
|
||||||
|
|
||||||
|
**How to reproduce**
|
||||||
|
If possible, provide information - possibly including code snippets - on how to reproduce the issue.
|
||||||
|
|
||||||
|
**Logs**
|
||||||
|
If possible, provide logs that indicate the issue. See https://github.com/Textalk/websocket-php/blob/master/docs/Examples.md#echo-logger on how to use the EchoLog.
|
||||||
|
|
||||||
|
**Versions**
|
||||||
|
* Version of this library
|
||||||
|
* PHP version
|
14
vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
14
vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this library
|
||||||
|
title: ''
|
||||||
|
labels: feature request
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is it within the scope of this library?**
|
||||||
|
Consider and describe why the feature would be beneficial in this library, and not implemented as a separate project using this as a dependency.
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
10
vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md
vendored
Normal file
10
vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: Other issue
|
||||||
|
about: Use this for other issues
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe your issue**
|
97
vendor/textalk/websocket/.github/workflows/acceptance.yml
vendored
Normal file
97
vendor/textalk/websocket/.github/workflows/acceptance.yml
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
name: Acceptance
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-7-4:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Test PHP 7.4
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP 7.4
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '7.4'
|
||||||
|
- name: Composer
|
||||||
|
run: make install
|
||||||
|
- name: Test
|
||||||
|
run: make test
|
||||||
|
|
||||||
|
test-8-0:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Test PHP 8.0
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP 8.0
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.0'
|
||||||
|
- name: Composer
|
||||||
|
run: make install
|
||||||
|
- name: Test
|
||||||
|
run: make test
|
||||||
|
|
||||||
|
test-8-1:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Test PHP 8.1
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP 8.1
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.1'
|
||||||
|
- name: Composer
|
||||||
|
run: make install
|
||||||
|
- name: Test
|
||||||
|
run: make test
|
||||||
|
|
||||||
|
test-8-2:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Test PHP 8.2
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP 8.2
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.2'
|
||||||
|
- name: Composer
|
||||||
|
run: make install
|
||||||
|
- name: Test
|
||||||
|
run: make test
|
||||||
|
|
||||||
|
cs-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Code standard
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP 8.0
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.0'
|
||||||
|
- name: Composer
|
||||||
|
run: make install
|
||||||
|
- name: Code standard
|
||||||
|
run: make cs-check
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Code coverage
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up PHP 8.0
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.0'
|
||||||
|
extensions: xdebug
|
||||||
|
- name: Composer
|
||||||
|
run: make install
|
||||||
|
- name: Code coverage
|
||||||
|
env:
|
||||||
|
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: make coverage
|
6
vendor/textalk/websocket/.gitignore
vendored
Normal file
6
vendor/textalk/websocket/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.DS_Store
|
||||||
|
.phpunit.result.cache
|
||||||
|
build/
|
||||||
|
composer.lock
|
||||||
|
composer.phar
|
||||||
|
vendor/
|
16
vendor/textalk/websocket/COPYING.md
vendored
Normal file
16
vendor/textalk/websocket/COPYING.md
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Websocket: License
|
||||||
|
|
||||||
|
Websocket PHP is free software released under the following license:
|
||||||
|
|
||||||
|
[ISC License](http://en.wikipedia.org/wiki/ISC_license)
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose with or without
|
||||||
|
fee is hereby granted, provided that the above copyright notice and this permission notice appear
|
||||||
|
in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
32
vendor/textalk/websocket/Makefile
vendored
Normal file
32
vendor/textalk/websocket/Makefile
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
install: composer.phar
|
||||||
|
./composer.phar install
|
||||||
|
|
||||||
|
update: composer.phar
|
||||||
|
./composer.phar self-update
|
||||||
|
./composer.phar update
|
||||||
|
|
||||||
|
test: composer.lock
|
||||||
|
./vendor/bin/phpunit
|
||||||
|
|
||||||
|
cs-check: composer.lock
|
||||||
|
./vendor/bin/phpcs --standard=PSR1,PSR12 --encoding=UTF-8 --report=full --colors lib tests examples
|
||||||
|
|
||||||
|
coverage: composer.lock build
|
||||||
|
XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
|
||||||
|
./vendor/bin/php-coveralls -v
|
||||||
|
|
||||||
|
composer.phar:
|
||||||
|
curl -s http://getcomposer.org/installer | php
|
||||||
|
|
||||||
|
composer.lock: composer.phar
|
||||||
|
./composer.phar --no-interaction install
|
||||||
|
|
||||||
|
vendor/bin/phpunit: install
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir build
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm composer.phar
|
||||||
|
rm -r vendor
|
||||||
|
rm -r build
|
76
vendor/textalk/websocket/README.md
vendored
Normal file
76
vendor/textalk/websocket/README.md
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Websocket Client and Server for PHP
|
||||||
|
|
||||||
|
[![Build Status](https://github.com/Textalk/websocket-php/actions/workflows/acceptance.yml/badge.svg)](https://github.com/Textalk/websocket-php/actions)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/Textalk/websocket-php/badge.svg?branch=master)](https://coveralls.io/github/Textalk/websocket-php)
|
||||||
|
|
||||||
|
## Archived project
|
||||||
|
|
||||||
|
This project has been archived and is no longer maintained. No bug fix and no additional features will be added.<br>
|
||||||
|
You won't be able to submit new issues or pull requests, and no additional features will be added
|
||||||
|
|
||||||
|
This library has been replaced by [sirn-se/websocket-php](https://github.com/sirn-se/websocket-php)
|
||||||
|
|
||||||
|
## Websocket Client and Server for PHP
|
||||||
|
|
||||||
|
This library contains WebSocket client and server for PHP.
|
||||||
|
|
||||||
|
The client and server provides methods for reading and writing to WebSocket streams.
|
||||||
|
It does not include convenience operations such as listeners and implicit error handling.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Client](docs/Client.md)
|
||||||
|
- [Server](docs/Server.md)
|
||||||
|
- [Examples](docs/Examples.md)
|
||||||
|
- [Changelog](docs/Changelog.md)
|
||||||
|
- [Contributing](docs/Contributing.md)
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
Preferred way to install is with [Composer](https://getcomposer.org/).
|
||||||
|
```
|
||||||
|
composer require textalk/websocket
|
||||||
|
```
|
||||||
|
|
||||||
|
* Current version support PHP versions `^7.4|^8.0`.
|
||||||
|
* For PHP `7.2` and `7.3` support use version [`1.5`](https://github.com/Textalk/websocket-php/tree/1.5.0).
|
||||||
|
* For PHP `7.1` support use version [`1.4`](https://github.com/Textalk/websocket-php/tree/1.4.0).
|
||||||
|
* For PHP `^5.4` and `7.0` support use version [`1.3`](https://github.com/Textalk/websocket-php/tree/1.3.0).
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
The [client](docs/Client.md) can read and write on a WebSocket stream.
|
||||||
|
It internally supports Upgrade handshake and implicit close and ping/pong operations.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/");
|
||||||
|
$client->text("Hello WebSocket.org!");
|
||||||
|
echo $client->receive();
|
||||||
|
$client->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server
|
||||||
|
|
||||||
|
The library contains a rudimentary single stream/single thread [server](docs/Server.md).
|
||||||
|
It internally supports Upgrade handshake and implicit close and ping/pong operations.
|
||||||
|
|
||||||
|
Note that it does **not** support threading or automatic association ot continuous client requests.
|
||||||
|
If you require this kind of server behavior, you need to build it on top of provided server implementation.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$server = new WebSocket\Server();
|
||||||
|
$server->accept();
|
||||||
|
$message = $server->receive();
|
||||||
|
$server->text($message);
|
||||||
|
$server->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
### License and Contributors
|
||||||
|
|
||||||
|
[ISC License](COPYING.md)
|
||||||
|
|
||||||
|
Fredrik Liljegren, Armen Baghumian Sankbarani, Ruslan Bekenev,
|
||||||
|
Joshua Thijssen, Simon Lipp, Quentin Bellus, Patrick McCarren, swmcdonnell,
|
||||||
|
Ignas Bernotas, Mark Herhold, Andreas Palm, Sören Jensen, pmaasz, Alexey Stavrov,
|
||||||
|
Michael Slezak, Pierre Seznec, rmeisler, Nickolay V. Shmyrev, Christoph Kempen,
|
||||||
|
Marc Roberts, Antonio Mora, Simon Podlipsky, etrinh.
|
36
vendor/textalk/websocket/composer.json
vendored
Normal file
36
vendor/textalk/websocket/composer.json
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "textalk/websocket",
|
||||||
|
"description": "WebSocket client and server",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "library",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fredrik Liljegren"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sören Jensen"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"WebSocket\\": "lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"WebSocket\\": "tests/mock"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 | ^8.0",
|
||||||
|
"phrity/net-uri": "^1.0",
|
||||||
|
"phrity/util-errorhandler": "^1.0",
|
||||||
|
"psr/log": "^1.0 | ^2.0 | ^3.0",
|
||||||
|
"psr/http-message": "^1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.0",
|
||||||
|
"php-coveralls/php-coveralls": "^2.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5"
|
||||||
|
}
|
||||||
|
}
|
167
vendor/textalk/websocket/docs/Changelog.md
vendored
Normal file
167
vendor/textalk/websocket/docs/Changelog.md
vendored
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • Changelog • [Contributing](Contributing.md)
|
||||||
|
|
||||||
|
# Websocket: Changelog
|
||||||
|
|
||||||
|
## `v1.6`
|
||||||
|
|
||||||
|
> PHP version `^7.4|^8.0`
|
||||||
|
|
||||||
|
### `1.6.3`
|
||||||
|
|
||||||
|
* Fix issue with implicit default ports (@etrinh, @sirn-se)
|
||||||
|
|
||||||
|
### `1.6.2`
|
||||||
|
|
||||||
|
* Fix issue where port was missing in socket uri (@sirn-se)
|
||||||
|
|
||||||
|
### `1.6.1`
|
||||||
|
|
||||||
|
* Fix client path for http request (@simPod, @sirn-se)
|
||||||
|
|
||||||
|
### `1.6.0`
|
||||||
|
* Connection separate from Client and Server (@sirn-se)
|
||||||
|
* getPier() deprecated, replaced by getRemoteName() (@sirn-se)
|
||||||
|
* Client accepts `Psr\Http\Message\UriInterface` as input for URI:s (@sirn-se)
|
||||||
|
* Bad URI throws exception when Client is instanciated, previously when used (@sirn-se)
|
||||||
|
* Preparations for multiple conection and listeners (@sirn-se)
|
||||||
|
* Major internal refactoring (@sirn-se)
|
||||||
|
|
||||||
|
## `v1.5`
|
||||||
|
|
||||||
|
> PHP version `^7.2|^8.0`
|
||||||
|
|
||||||
|
### `1.5.8`
|
||||||
|
|
||||||
|
* Handle read error during handshake (@sirn-se)
|
||||||
|
|
||||||
|
### `1.5.7`
|
||||||
|
|
||||||
|
* Large header block fix (@sirn-se)
|
||||||
|
|
||||||
|
### `1.5.6`
|
||||||
|
|
||||||
|
* Add test for PHP 8.1 (@sirn-se)
|
||||||
|
* Code standard (@sirn-se)
|
||||||
|
|
||||||
|
### `1.5.5`
|
||||||
|
|
||||||
|
* Support for psr/log v2 and v3 (@simPod)
|
||||||
|
* GitHub Actions replaces Travis (@sirn-se)
|
||||||
|
|
||||||
|
### `1.5.4`
|
||||||
|
|
||||||
|
* Keep open connection on read timeout (@marcroberts)
|
||||||
|
|
||||||
|
### `1.5.3`
|
||||||
|
|
||||||
|
* Fix for persistent connection (@sirn-se)
|
||||||
|
|
||||||
|
### `1.5.2`
|
||||||
|
|
||||||
|
* Fix for getName() method (@sirn-se)
|
||||||
|
|
||||||
|
### `1.5.1`
|
||||||
|
|
||||||
|
* Fix for persistent connections (@rmeisler)
|
||||||
|
|
||||||
|
### `1.5.0`
|
||||||
|
|
||||||
|
* Convenience send methods; text(), binary(), ping(), pong() (@sirn-se)
|
||||||
|
* Optional Message instance as receive() method return (@sirn-se)
|
||||||
|
* Opcode filter for receive() method (@sirn-se)
|
||||||
|
* Added PHP `8.0` support (@webpatser)
|
||||||
|
* Dropped PHP `7.1` support (@sirn-se)
|
||||||
|
* Fix for unordered fragmented messages (@sirn-se)
|
||||||
|
* Improved error handling on stream calls (@sirn-se)
|
||||||
|
* Various code re-write (@sirn-se)
|
||||||
|
|
||||||
|
## `v1.4`
|
||||||
|
|
||||||
|
> PHP version `^7.1`
|
||||||
|
|
||||||
|
#### `1.4.3`
|
||||||
|
|
||||||
|
* Solve stream closure/get meta conflict (@sirn-se)
|
||||||
|
* Examples and documentation overhaul (@sirn-se)
|
||||||
|
|
||||||
|
#### `1.4.2`
|
||||||
|
|
||||||
|
* Force stream close on read error (@sirn-se)
|
||||||
|
* Authorization headers line feed (@sirn-se)
|
||||||
|
* Documentation (@matias-pool, @sirn-se)
|
||||||
|
|
||||||
|
#### `1.4.1`
|
||||||
|
|
||||||
|
* Ping/Pong, handled internally to avoid breaking fragmented messages (@nshmyrev, @sirn-se)
|
||||||
|
* Fix for persistent connections (@rmeisler)
|
||||||
|
* Fix opcode bitmask (@peterjah)
|
||||||
|
|
||||||
|
#### `1.4.0`
|
||||||
|
|
||||||
|
* Dropped support of old PHP versions (@sirn-se)
|
||||||
|
* Added PSR-3 Logging support (@sirn-se)
|
||||||
|
* Persistent connection option (@slezakattack)
|
||||||
|
* TimeoutException on connection time out (@slezakattack)
|
||||||
|
|
||||||
|
## `v1.3`
|
||||||
|
|
||||||
|
> PHP version `^5.4` and `^7.0`
|
||||||
|
|
||||||
|
#### `1.3.1`
|
||||||
|
|
||||||
|
* Allow control messages without payload (@Logioniz)
|
||||||
|
* Error code in ConnectionException (@sirn-se)
|
||||||
|
|
||||||
|
#### `1.3.0`
|
||||||
|
|
||||||
|
* Implements ping/pong frames (@pmccarren @Logioniz)
|
||||||
|
* Close behaviour (@sirn-se)
|
||||||
|
* Various fixes concerning connection handling (@sirn-se)
|
||||||
|
* Overhaul of Composer, Travis and Coveralls setup, PSR code standard and unit tests (@sirn-se)
|
||||||
|
|
||||||
|
## `v1.2`
|
||||||
|
|
||||||
|
> PHP version `^5.4` and `^7.0`
|
||||||
|
|
||||||
|
#### `1.2.0`
|
||||||
|
|
||||||
|
* Adding stream context options (to set e.g. SSL `allow_self_signed`).
|
||||||
|
|
||||||
|
## `v1.1`
|
||||||
|
|
||||||
|
> PHP version `^5.4` and `^7.0`
|
||||||
|
|
||||||
|
#### `1.1.2`
|
||||||
|
|
||||||
|
* Fixed error message on broken frame.
|
||||||
|
|
||||||
|
#### `1.1.1`
|
||||||
|
|
||||||
|
* Adding license information.
|
||||||
|
|
||||||
|
#### `1.1.0`
|
||||||
|
|
||||||
|
* Supporting huge payloads.
|
||||||
|
|
||||||
|
## `v1.0`
|
||||||
|
|
||||||
|
> PHP version `^5.4` and `^7.0`
|
||||||
|
|
||||||
|
#### `1.0.3`
|
||||||
|
|
||||||
|
* Bugfix: Correcting address in error-message
|
||||||
|
|
||||||
|
#### `1.0.2`
|
||||||
|
|
||||||
|
* Bugfix: Add port in request-header.
|
||||||
|
|
||||||
|
#### `1.0.1`
|
||||||
|
|
||||||
|
* Fixing a bug from empty payloads.
|
||||||
|
|
||||||
|
#### `1.0.0`
|
||||||
|
|
||||||
|
* Release as production ready.
|
||||||
|
* Adding option to set/override headers.
|
||||||
|
* Supporting basic authentication from user:pass in URL.
|
||||||
|
|
137
vendor/textalk/websocket/docs/Client.md
vendored
Normal file
137
vendor/textalk/websocket/docs/Client.md
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
Client • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md)
|
||||||
|
|
||||||
|
# Websocket: Client
|
||||||
|
|
||||||
|
The client can read and write on a WebSocket stream.
|
||||||
|
It internally supports Upgrade handshake and implicit close and ping/pong operations.
|
||||||
|
|
||||||
|
## Class synopsis
|
||||||
|
|
||||||
|
```php
|
||||||
|
WebSocket\Client {
|
||||||
|
|
||||||
|
public __construct(UriInterface|string $uri, array $options = []);
|
||||||
|
public __destruct();
|
||||||
|
public __toString() : string;
|
||||||
|
|
||||||
|
public text(string $payload) : void;
|
||||||
|
public binary(string $payload) : void;
|
||||||
|
public ping(string $payload = '') : void;
|
||||||
|
public pong(string $payload = '') : void;
|
||||||
|
public send(Message|string $payload, string $opcode = 'text', bool $masked = true) : void;
|
||||||
|
public close(int $status = 1000, mixed $message = 'ttfn') : void;
|
||||||
|
public receive() : Message|string|null;
|
||||||
|
|
||||||
|
public getName() : string|null;
|
||||||
|
public getRemoteName() : string|null;
|
||||||
|
public getLastOpcode() : string;
|
||||||
|
public getCloseStatus() : int;
|
||||||
|
public isConnected() : bool;
|
||||||
|
public setTimeout(int $seconds) : void;
|
||||||
|
public setFragmentSize(int $fragment_size) : self;
|
||||||
|
public getFragmentSize() : int;
|
||||||
|
public setLogger(Psr\Log\LoggerInterface $logger = null) : void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Simple send-receive operation
|
||||||
|
|
||||||
|
This example send a single message to a server, and output the response.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/");
|
||||||
|
$client->text("Hello WebSocket.org!");
|
||||||
|
echo $client->receive();
|
||||||
|
$client->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listening to a server
|
||||||
|
|
||||||
|
To continuously listen to incoming messages, you need to put the receive operation within a loop.
|
||||||
|
Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out.
|
||||||
|
By consuming exceptions, the code will re-connect the socket in next loop iteration.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/");
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
$message = $client->receive();
|
||||||
|
// Act on received message
|
||||||
|
// Break while loop to stop listening
|
||||||
|
} catch (\WebSocket\ConnectionException $e) {
|
||||||
|
// Possibly log errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$client->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtering received messages
|
||||||
|
|
||||||
|
By default the `receive()` method return messages of 'text' and 'binary' opcode.
|
||||||
|
The filter option allows you to specify which message types to return.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/", ['filter' => ['text']]);
|
||||||
|
$client->receive(); // Only return 'text' messages
|
||||||
|
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/", ['filter' => ['text', 'binary', 'ping', 'pong', 'close']]);
|
||||||
|
$client->receive(); // Return all messages
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sending messages
|
||||||
|
|
||||||
|
There are convenience methods to send messages with different opcodes.
|
||||||
|
```php
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/");
|
||||||
|
|
||||||
|
// Convenience methods
|
||||||
|
$client->text('A plain text message'); // Send an opcode=text message
|
||||||
|
$client->binary($binary_string); // Send an opcode=binary message
|
||||||
|
$client->ping(); // Send an opcode=ping frame
|
||||||
|
$client->pong(); // Send an unsolicited opcode=pong frame
|
||||||
|
|
||||||
|
// Generic send method
|
||||||
|
$client->send($payload); // Sent as masked opcode=text
|
||||||
|
$client->send($payload, 'binary'); // Sent as masked opcode=binary
|
||||||
|
$client->send($payload, 'binary', false); // Sent as unmasked opcode=binary
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constructor options
|
||||||
|
|
||||||
|
The `$options` parameter in constructor accepts an associative array of options.
|
||||||
|
|
||||||
|
* `context` - A stream context created using [stream_context_create](https://www.php.net/manual/en/function.stream-context-create).
|
||||||
|
* `filter` - Array of opcodes to return on receive, default `['text', 'binary']`
|
||||||
|
* `fragment_size` - Maximum payload size. Default 4096 chars.
|
||||||
|
* `headers` - Additional headers as associative array name => content.
|
||||||
|
* `logger` - A [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger.
|
||||||
|
* `persistent` - Connection is re-used between requests until time out is reached. Default false.
|
||||||
|
* `return_obj` - Return a [Message](Message.md) instance on receive, default false
|
||||||
|
* `timeout` - Time out in seconds. Default 5 seconds.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$context = stream_context_create();
|
||||||
|
stream_context_set_option($context, 'ssl', 'verify_peer', false);
|
||||||
|
stream_context_set_option($context, 'ssl', 'verify_peer_name', false);
|
||||||
|
|
||||||
|
$client = new WebSocket\Client("ws://echo.websocket.org/", [
|
||||||
|
'context' => $context, // Attach stream context created above
|
||||||
|
'filter' => ['text', 'binary', 'ping'], // Specify message types for receive() to return
|
||||||
|
'headers' => [ // Additional headers, used to specify subprotocol
|
||||||
|
'Sec-WebSocket-Protocol' => 'soap',
|
||||||
|
'origin' => 'localhost',
|
||||||
|
],
|
||||||
|
'logger' => $my_psr3_logger, // Attach a PSR3 compatible logger
|
||||||
|
'return_obj' => true, // Return Message instance rather than just text
|
||||||
|
'timeout' => 60, // 1 minute time out
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid.
|
||||||
|
* `WebSocket\BadUriException` - Thrown if provided URI is invalid.
|
||||||
|
* `WebSocket\ConnectionException` - Thrown on any socket I/O failure.
|
||||||
|
* `WebSocket\TimeoutException` - Thrown when the socket experiences a time out.
|
51
vendor/textalk/websocket/docs/Contributing.md
vendored
Normal file
51
vendor/textalk/websocket/docs/Contributing.md
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • Contributing
|
||||||
|
|
||||||
|
# Websocket: Contributing
|
||||||
|
|
||||||
|
Everyone is welcome to help out!
|
||||||
|
But to keep this project sustainable, please ensure your contribution respects the requirements below.
|
||||||
|
|
||||||
|
## PR Requirements
|
||||||
|
|
||||||
|
Requirements on pull requests;
|
||||||
|
* All tests **MUST** pass.
|
||||||
|
* Code coverage **MUST** remain at 100%.
|
||||||
|
* Code **MUST** adhere to PSR-1 and PSR-12 code standards.
|
||||||
|
|
||||||
|
Base your patch on corresponding version branch, and target that version branch in your pull request.
|
||||||
|
|
||||||
|
* `v1.6-master` current version
|
||||||
|
* `v1.5-master` previous version, bug fixes only
|
||||||
|
* Older versions should not be target of pull requests
|
||||||
|
|
||||||
|
|
||||||
|
## Dependency management
|
||||||
|
|
||||||
|
Install or update dependencies using [Composer](https://getcomposer.org/).
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
make install
|
||||||
|
|
||||||
|
# Update dependencies
|
||||||
|
make update
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code standard
|
||||||
|
|
||||||
|
This project uses [PSR-1](https://www.php-fig.org/psr/psr-1/) and [PSR-12](https://www.php-fig.org/psr/psr-12/) code standards.
|
||||||
|
```
|
||||||
|
# Check code standard adherence
|
||||||
|
make cs-check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unit testing
|
||||||
|
|
||||||
|
Unit tests with [PHPUnit](https://phpunit.readthedocs.io/), coverage with [Coveralls](https://github.com/php-coveralls/php-coveralls)
|
||||||
|
```
|
||||||
|
# Run unit tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Create coverage
|
||||||
|
make coverage
|
||||||
|
```
|
101
vendor/textalk/websocket/docs/Examples.md
vendored
Normal file
101
vendor/textalk/websocket/docs/Examples.md
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • Examples • [Changelog](Changelog.md) • [Contributing](Contributing.md)
|
||||||
|
|
||||||
|
# Websocket: Examples
|
||||||
|
|
||||||
|
Here are some examples on how to use the WebSocket library.
|
||||||
|
|
||||||
|
## Echo logger
|
||||||
|
|
||||||
|
In dev environment (as in having run composer to include dev dependencies) you have
|
||||||
|
access to a simple echo logger that print out information synchronously.
|
||||||
|
|
||||||
|
This is usable for debugging. For production, use a proper logger.
|
||||||
|
|
||||||
|
```php
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
$logger = new EchoLogger();
|
||||||
|
|
||||||
|
$client = new Client('ws://echo.websocket.org/');
|
||||||
|
$client->setLogger($logger);
|
||||||
|
|
||||||
|
$server = new Server();
|
||||||
|
$server->setLogger($logger);
|
||||||
|
```
|
||||||
|
|
||||||
|
An example of server output;
|
||||||
|
```
|
||||||
|
info | Server listening to port 8000 []
|
||||||
|
debug | Wrote 129 of 129 bytes. []
|
||||||
|
info | Server connected to port 8000 []
|
||||||
|
info | Received 'text' message []
|
||||||
|
debug | Wrote 9 of 9 bytes. []
|
||||||
|
info | Sent 'text' message []
|
||||||
|
debug | Received 'close', status: 1000. []
|
||||||
|
debug | Wrote 32 of 32 bytes. []
|
||||||
|
info | Sent 'close' message []
|
||||||
|
info | Received 'close' message []
|
||||||
|
```
|
||||||
|
|
||||||
|
## The `send` client
|
||||||
|
|
||||||
|
Source: [examples/send.php](../examples/send.php)
|
||||||
|
|
||||||
|
A simple, single send/receive client.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
```
|
||||||
|
php examples/send.php --opcode text "A text message" // Send a text message to localhost
|
||||||
|
php examples/send.php --opcode ping "ping it" // Send a ping message to localhost
|
||||||
|
php examples/send.php --uri ws://echo.websocket.org "A text message" // Send a text message to echo.websocket.org
|
||||||
|
php examples/send.php --opcode text --debug "A text message" // Use runtime debugging
|
||||||
|
```
|
||||||
|
|
||||||
|
## The `echoserver` server
|
||||||
|
|
||||||
|
Source: [examples/echoserver.php](../examples/echoserver.php)
|
||||||
|
|
||||||
|
A simple server that responds to recevied commands.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
```
|
||||||
|
php examples/echoserver.php // Run with default settings
|
||||||
|
php examples/echoserver.php --port 8080 // Listen on port 8080
|
||||||
|
php examples/echoserver.php --debug // Use runtime debugging
|
||||||
|
```
|
||||||
|
|
||||||
|
These strings can be sent as message to trigger server to perform actions;
|
||||||
|
* `auth` - Server will respond with auth header if provided by client
|
||||||
|
* `close` - Server will close current connection
|
||||||
|
* `exit` - Server will close all active connections
|
||||||
|
* `headers` - Server will respond with all headers provided by client
|
||||||
|
* `ping` - Server will send a ping message
|
||||||
|
* `pong` - Server will send a pong message
|
||||||
|
* `stop` - Server will stop listening
|
||||||
|
* For other sent strings, server will respond with the same strings
|
||||||
|
|
||||||
|
## The `random` client
|
||||||
|
|
||||||
|
Source: [examples/random_client.php](../examples/random_client.php)
|
||||||
|
|
||||||
|
The random client will use random options and continuously send/receive random messages.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
```
|
||||||
|
php examples/random_client.php --uri ws://echo.websocket.org // Connect to echo.websocket.org
|
||||||
|
php examples/random_client.php --timeout 5 --fragment_size 16 // Specify settings
|
||||||
|
php examples/random_client.php --debug // Use runtime debugging
|
||||||
|
```
|
||||||
|
|
||||||
|
## The `random` server
|
||||||
|
|
||||||
|
Source: [examples/random_server.php](../examples/random_server.php)
|
||||||
|
|
||||||
|
The random server will use random options and continuously send/receive random messages.
|
||||||
|
|
||||||
|
Example use:
|
||||||
|
```
|
||||||
|
php examples/random_server.php --port 8080 // // Listen on port 8080
|
||||||
|
php examples/random_server.php --timeout 5 --fragment_size 16 // Specify settings
|
||||||
|
php examples/random_server.php --debug // Use runtime debugging
|
||||||
|
```
|
60
vendor/textalk/websocket/docs/Message.md
vendored
Normal file
60
vendor/textalk/websocket/docs/Message.md
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
[Client](Client.md) • [Server](Server.md) • Message • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md)
|
||||||
|
|
||||||
|
# Websocket: Messages
|
||||||
|
|
||||||
|
If option `return_obj` is set to `true` on [client](Client.md) or [server](Server.md),
|
||||||
|
the `receive()` method will return a Message instance instead of a string.
|
||||||
|
|
||||||
|
Available classes correspond to opcode;
|
||||||
|
* WebSocket\Message\Text
|
||||||
|
* WebSocket\Message\Binary
|
||||||
|
* WebSocket\Message\Ping
|
||||||
|
* WebSocket\Message\Pong
|
||||||
|
* WebSocket\Message\Close
|
||||||
|
|
||||||
|
Additionally;
|
||||||
|
* WebSocket\Message\Message - abstract base class for all messages above
|
||||||
|
* WebSocket\Message\Factory - Factory class to create Message instances
|
||||||
|
|
||||||
|
## Message abstract class synopsis
|
||||||
|
|
||||||
|
```php
|
||||||
|
WebSocket\Message\Message {
|
||||||
|
|
||||||
|
public __construct(string $payload = '');
|
||||||
|
public __toString() : string;
|
||||||
|
|
||||||
|
public getOpcode() : string;
|
||||||
|
public getLength() : int;
|
||||||
|
public getTimestamp() : DateTime;
|
||||||
|
public getContent() : string;
|
||||||
|
public setContent(string $payload = '') : void;
|
||||||
|
public hasContent() : bool;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Factory class synopsis
|
||||||
|
|
||||||
|
```php
|
||||||
|
WebSocket\Message\Factory {
|
||||||
|
|
||||||
|
public create(string $opcode, string $payload = '') : Message;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Receving a Message and echo some methods.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$client = new WebSocket\Client('ws://echo.websocket.org/', ['return_obj' => true]);
|
||||||
|
$client->text('Hello WebSocket.org!');
|
||||||
|
// Echo return same message as sent
|
||||||
|
$message = $client->receive();
|
||||||
|
echo $message->getOpcode(); // -> "text"
|
||||||
|
echo $message->getLength(); // -> 20
|
||||||
|
echo $message->getContent(); // -> "Hello WebSocket.org!"
|
||||||
|
echo $message->hasContent(); // -> true
|
||||||
|
echo $message->getTimestamp()->format('H:i:s'); // -> 19:37:18
|
||||||
|
$client->close();
|
||||||
|
```
|
136
vendor/textalk/websocket/docs/Server.md
vendored
Normal file
136
vendor/textalk/websocket/docs/Server.md
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
[Client](Client.md) • Server • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md)
|
||||||
|
|
||||||
|
# Websocket: Server
|
||||||
|
|
||||||
|
The library contains a rudimentary single stream/single thread server.
|
||||||
|
It internally supports Upgrade handshake and implicit close and ping/pong operations.
|
||||||
|
|
||||||
|
Note that it does **not** support threading or automatic association ot continuous client requests.
|
||||||
|
If you require this kind of server behavior, you need to build it on top of provided server implementation.
|
||||||
|
|
||||||
|
## Class synopsis
|
||||||
|
|
||||||
|
```php
|
||||||
|
WebSocket\Server {
|
||||||
|
|
||||||
|
public __construct(array $options = []);
|
||||||
|
public __destruct();
|
||||||
|
public __toString() : string;
|
||||||
|
|
||||||
|
public accept() : bool;
|
||||||
|
public text(string $payload) : void;
|
||||||
|
public binary(string $payload) : void;
|
||||||
|
public ping(string $payload = '') : void;
|
||||||
|
public pong(string $payload = '') : void;
|
||||||
|
public send(Message|string $payload, string $opcode = 'text', bool $masked = true) : void;
|
||||||
|
public close(int $status = 1000, mixed $message = 'ttfn') : void;
|
||||||
|
public receive() : Message|string|null;
|
||||||
|
|
||||||
|
public getPort() : int;
|
||||||
|
public getPath() : string;
|
||||||
|
public getRequest() : array;
|
||||||
|
public getHeader(string $header_name) : string|null;
|
||||||
|
|
||||||
|
public getName() : string|null;
|
||||||
|
public getRemoteName() : string|null;
|
||||||
|
public getLastOpcode() : string;
|
||||||
|
public getCloseStatus() : int;
|
||||||
|
public isConnected() : bool;
|
||||||
|
public setTimeout(int $seconds) : void;
|
||||||
|
public setFragmentSize(int $fragment_size) : self;
|
||||||
|
public getFragmentSize() : int;
|
||||||
|
public setLogger(Psr\Log\LoggerInterface $logger = null) : void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Simple receive-send operation
|
||||||
|
|
||||||
|
This example reads a single message from a client, and respond with the same message.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$server = new WebSocket\Server();
|
||||||
|
$server->accept();
|
||||||
|
$message = $server->receive();
|
||||||
|
$server->text($message);
|
||||||
|
$server->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Listening to clients
|
||||||
|
|
||||||
|
To continuously listen to incoming messages, you need to put the receive operation within a loop.
|
||||||
|
Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out.
|
||||||
|
By consuming exceptions, the code will re-connect the socket in next loop iteration.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$server = new WebSocket\Server();
|
||||||
|
while ($server->accept()) {
|
||||||
|
try {
|
||||||
|
$message = $server->receive();
|
||||||
|
// Act on received message
|
||||||
|
// Break while loop to stop listening
|
||||||
|
} catch (\WebSocket\ConnectionException $e) {
|
||||||
|
// Possibly log errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$server->close();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtering received messages
|
||||||
|
|
||||||
|
By default the `receive()` method return messages of 'text' and 'binary' opcode.
|
||||||
|
The filter option allows you to specify which message types to return.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$server = new WebSocket\Server(['filter' => ['text']]);
|
||||||
|
$server->receive(); // only return 'text' messages
|
||||||
|
|
||||||
|
$server = new WebSocket\Server(['filter' => ['text', 'binary', 'ping', 'pong', 'close']]);
|
||||||
|
$server->receive(); // return all messages
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sending messages
|
||||||
|
|
||||||
|
There are convenience methods to send messages with different opcodes.
|
||||||
|
```php
|
||||||
|
$server = new WebSocket\Server();
|
||||||
|
|
||||||
|
// Convenience methods
|
||||||
|
$server->text('A plain text message'); // Send an opcode=text message
|
||||||
|
$server->binary($binary_string); // Send an opcode=binary message
|
||||||
|
$server->ping(); // Send an opcode=ping frame
|
||||||
|
$server->pong(); // Send an unsolicited opcode=pong frame
|
||||||
|
|
||||||
|
// Generic send method
|
||||||
|
$server->send($payload); // Sent as masked opcode=text
|
||||||
|
$server->send($payload, 'binary'); // Sent as masked opcode=binary
|
||||||
|
$server->send($payload, 'binary', false); // Sent as unmasked opcode=binary
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constructor options
|
||||||
|
|
||||||
|
The `$options` parameter in constructor accepts an associative array of options.
|
||||||
|
|
||||||
|
* `filter` - Array of opcodes to return on receive, default `['text', 'binary']`
|
||||||
|
* `fragment_size` - Maximum payload size. Default 4096 chars.
|
||||||
|
* `logger` - A [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger.
|
||||||
|
* `port` - The server port to listen to. Default 8000.
|
||||||
|
* `return_obj` - Return a [Message](Message.md) instance on receive, default false
|
||||||
|
* `timeout` - Time out in seconds. Default 5 seconds.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$server = new WebSocket\Server([
|
||||||
|
'filter' => ['text', 'binary', 'ping'], // Specify message types for receive() to return
|
||||||
|
'logger' => $my_psr3_logger, // Attach a PSR3 compatible logger
|
||||||
|
'port' => 9000, // Listening port
|
||||||
|
'return_obj' => true, // Return Message instance rather than just text
|
||||||
|
'timeout' => 60, // 1 minute time out
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid.
|
||||||
|
* `WebSocket\ConnectionException` - Thrown on any socket I/O failure.
|
||||||
|
* `WebSocket\TimeoutException` - Thrown when the socket experiences a time out.
|
87
vendor/textalk/websocket/examples/echoserver.php
vendored
Normal file
87
vendor/textalk/websocket/examples/echoserver.php
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is used for the tests, but can also serve as an example of a WebSocket\Server.
|
||||||
|
* Run in console: php examples/echoserver.php
|
||||||
|
*
|
||||||
|
* Console options:
|
||||||
|
* --port <int> : The port to listen to, default 8000
|
||||||
|
* --timeout <int> : Timeout in seconds, default 200 seconds
|
||||||
|
* --debug : Output log data (if logger is available)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
echo "> Echo server\n";
|
||||||
|
|
||||||
|
// Server options specified or random
|
||||||
|
$options = array_merge([
|
||||||
|
'port' => 8000,
|
||||||
|
'timeout' => 200,
|
||||||
|
'filter' => ['text', 'binary', 'ping', 'pong', 'close'],
|
||||||
|
], getopt('', ['port:', 'timeout:', 'debug']));
|
||||||
|
|
||||||
|
// If debug mode and logger is available
|
||||||
|
if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) {
|
||||||
|
$logger = new EchoLog();
|
||||||
|
$options['logger'] = $logger;
|
||||||
|
echo "> Using logger\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate server.
|
||||||
|
try {
|
||||||
|
$server = new Server($options);
|
||||||
|
} catch (ConnectionException $e) {
|
||||||
|
echo "> ERROR: {$e->getMessage()}\n";
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "> Listening to port {$server->getPort()}\n";
|
||||||
|
|
||||||
|
// Force quit to close server
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
while ($server->accept()) {
|
||||||
|
echo "> Accepted on port {$server->getPort()}\n";
|
||||||
|
while (true) {
|
||||||
|
$message = $server->receive();
|
||||||
|
$opcode = $server->getLastOpcode();
|
||||||
|
if (is_null($message)) {
|
||||||
|
echo "> Closing connection\n";
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
echo "> Got '{$message}' [opcode: {$opcode}]\n";
|
||||||
|
if (in_array($opcode, ['ping', 'pong'])) {
|
||||||
|
$server->send($message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Allow certain string to trigger server action
|
||||||
|
switch ($message) {
|
||||||
|
case 'exit':
|
||||||
|
echo "> Client told me to quit. Bye bye.\n";
|
||||||
|
$server->close();
|
||||||
|
echo "> Close status: {$server->getCloseStatus()}\n";
|
||||||
|
exit;
|
||||||
|
case 'headers':
|
||||||
|
$server->text(implode("\r\n", $server->getRequest()));
|
||||||
|
break;
|
||||||
|
case 'ping':
|
||||||
|
$server->ping($message);
|
||||||
|
break;
|
||||||
|
case 'auth':
|
||||||
|
$auth = $server->getHeader('Authorization');
|
||||||
|
$server->text("{$auth} - {$message}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$server->text($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ConnectionException $e) {
|
||||||
|
echo "> ERROR: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
}
|
94
vendor/textalk/websocket/examples/random_client.php
vendored
Normal file
94
vendor/textalk/websocket/examples/random_client.php
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Websocket client that read/write random data.
|
||||||
|
* Run in console: php examples/random_client.php
|
||||||
|
*
|
||||||
|
* Console options:
|
||||||
|
* --uri <uri> : The URI to connect to, default ws://localhost:8000
|
||||||
|
* --timeout <int> : Timeout in seconds, random default
|
||||||
|
* --fragment_size <int> : Fragment size as bytes, random default
|
||||||
|
* --debug : Output log data (if logger is available)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
$randStr = function (int $maxlength = 4096) {
|
||||||
|
$string = '';
|
||||||
|
$length = rand(1, $maxlength);
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$string .= chr(rand(33, 126));
|
||||||
|
}
|
||||||
|
return $string;
|
||||||
|
};
|
||||||
|
|
||||||
|
echo "> Random client\n";
|
||||||
|
|
||||||
|
// Server options specified or random
|
||||||
|
$options = array_merge([
|
||||||
|
'uri' => 'ws://localhost:8000',
|
||||||
|
'timeout' => rand(1, 60),
|
||||||
|
'fragment_size' => rand(1, 4096) * 8,
|
||||||
|
], getopt('', ['uri:', 'timeout:', 'fragment_size:', 'debug']));
|
||||||
|
|
||||||
|
// If debug mode and logger is available
|
||||||
|
if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) {
|
||||||
|
$logger = new EchoLog();
|
||||||
|
$options['logger'] = $logger;
|
||||||
|
echo "> Using logger\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
$client = new Client($options['uri'], $options);
|
||||||
|
$info = json_encode([
|
||||||
|
'uri' => $options['uri'],
|
||||||
|
'timeout' => $options['timeout'],
|
||||||
|
'framgemt_size' => $client->getFragmentSize(),
|
||||||
|
]);
|
||||||
|
echo "> Creating client {$info}\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
// Random actions
|
||||||
|
switch (rand(1, 10)) {
|
||||||
|
case 1:
|
||||||
|
echo "> Sending text\n";
|
||||||
|
$client->text("Text message {$randStr()}");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
echo "> Sending binary\n";
|
||||||
|
$client->binary("Binary message {$randStr()}");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
echo "> Sending close\n";
|
||||||
|
$client->close(rand(1000, 2000), "Close message {$randStr(8)}");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
echo "> Sending ping\n";
|
||||||
|
$client->ping("Ping message {$randStr(8)}");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
echo "> Sending pong\n";
|
||||||
|
$client->pong("Pong message {$randStr(8)}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo "> Receiving\n";
|
||||||
|
$received = $client->receive();
|
||||||
|
echo "> Received {$client->getLastOpcode()}: {$received}\n";
|
||||||
|
}
|
||||||
|
sleep(rand(1, 5));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "ERROR I/O: {$e->getMessage()} [{$e->getCode()}]\n";
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "ERROR: {$e->getMessage()} [{$e->getCode()}]\n";
|
||||||
|
}
|
||||||
|
sleep(rand(1, 5));
|
||||||
|
}
|
93
vendor/textalk/websocket/examples/random_server.php
vendored
Normal file
93
vendor/textalk/websocket/examples/random_server.php
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Websocket server that read/write random data.
|
||||||
|
* Run in console: php examples/random_server.php
|
||||||
|
*
|
||||||
|
* Console options:
|
||||||
|
* --port <int> : The port to listen to, default 8000
|
||||||
|
* --timeout <int> : Timeout in seconds, random default
|
||||||
|
* --fragment_size <int> : Fragment size as bytes, random default
|
||||||
|
* --debug : Output log data (if logger is available)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
$randStr = function (int $maxlength = 4096) {
|
||||||
|
$string = '';
|
||||||
|
$length = rand(1, $maxlength);
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$string .= chr(rand(33, 126));
|
||||||
|
}
|
||||||
|
return $string;
|
||||||
|
};
|
||||||
|
|
||||||
|
echo "> Random server\n";
|
||||||
|
|
||||||
|
// Server options specified or random
|
||||||
|
$options = array_merge([
|
||||||
|
'port' => 8000,
|
||||||
|
'timeout' => rand(1, 60),
|
||||||
|
'fragment_size' => rand(1, 4096) * 8,
|
||||||
|
], getopt('', ['port:', 'timeout:', 'fragment_size:', 'debug']));
|
||||||
|
|
||||||
|
// If debug mode and logger is available
|
||||||
|
if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) {
|
||||||
|
$logger = new EchoLog();
|
||||||
|
$options['logger'] = $logger;
|
||||||
|
echo "> Using logger\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force quit to close server
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
// Setup server
|
||||||
|
$server = new Server($options);
|
||||||
|
$info = json_encode([
|
||||||
|
'port' => $server->getPort(),
|
||||||
|
'timeout' => $options['timeout'],
|
||||||
|
'framgemt_size' => $server->getFragmentSize(),
|
||||||
|
]);
|
||||||
|
echo "> Creating server {$info}\n";
|
||||||
|
|
||||||
|
while ($server->accept()) {
|
||||||
|
while (true) {
|
||||||
|
// Random actions
|
||||||
|
switch (rand(1, 10)) {
|
||||||
|
case 1:
|
||||||
|
echo "> Sending text\n";
|
||||||
|
$server->text("Text message {$randStr()}");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
echo "> Sending binary\n";
|
||||||
|
$server->binary("Binary message {$randStr()}");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
echo "> Sending close\n";
|
||||||
|
$server->close(rand(1000, 2000), "Close message {$randStr(8)}");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
echo "> Sending ping\n";
|
||||||
|
$server->ping("Ping message {$randStr(8)}");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
echo "> Sending pong\n";
|
||||||
|
$server->pong("Pong message {$randStr(8)}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo "> Receiving\n";
|
||||||
|
$received = $server->receive();
|
||||||
|
echo "> Received {$server->getLastOpcode()}: {$received}\n";
|
||||||
|
}
|
||||||
|
sleep(rand(1, 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "ERROR: {$e->getMessage()} [{$e->getCode()}]\n";
|
||||||
|
}
|
||||||
|
sleep(rand(1, 5));
|
||||||
|
}
|
51
vendor/textalk/websocket/examples/send.php
vendored
Normal file
51
vendor/textalk/websocket/examples/send.php
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple send & receive client for test purpose.
|
||||||
|
* Run in console: php examples/send.php <options> <message>
|
||||||
|
*
|
||||||
|
* Console options:
|
||||||
|
* --uri <uri> : The URI to connect to, default ws://localhost:8000
|
||||||
|
* --opcode <string> : Opcode to send, default 'text'
|
||||||
|
* --debug : Output log data (if logger is available)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
|
||||||
|
echo "> Send client\n";
|
||||||
|
|
||||||
|
// Server options specified or random
|
||||||
|
$options = array_merge([
|
||||||
|
'uri' => 'ws://localhost:8000',
|
||||||
|
'opcode' => 'text',
|
||||||
|
], getopt('', ['uri:', 'opcode:', 'debug']));
|
||||||
|
$message = array_pop($argv);
|
||||||
|
|
||||||
|
// If debug mode and logger is available
|
||||||
|
if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) {
|
||||||
|
$logger = new EchoLog();
|
||||||
|
$options['logger'] = $logger;
|
||||||
|
echo "> Using logger\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create client, send and recevie
|
||||||
|
$client = new Client($options['uri'], $options);
|
||||||
|
$client->send($message, $options['opcode']);
|
||||||
|
echo "> Sent '{$message}' [opcode: {$options['opcode']}]\n";
|
||||||
|
if (in_array($options['opcode'], ['text', 'binary'])) {
|
||||||
|
$message = $client->receive();
|
||||||
|
$opcode = $client->getLastOpcode();
|
||||||
|
if (!is_null($message)) {
|
||||||
|
echo "> Got '{$message}' [opcode: {$opcode}]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$client->close();
|
||||||
|
echo "> Closing client\n";
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "ERROR: {$e->getMessage()} [{$e->getCode()}]\n";
|
||||||
|
}
|
14
vendor/textalk/websocket/lib/BadOpcodeException.php
vendored
Normal file
14
vendor/textalk/websocket/lib/BadOpcodeException.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
class BadOpcodeException extends Exception
|
||||||
|
{
|
||||||
|
}
|
7
vendor/textalk/websocket/lib/BadUriException.php
vendored
Normal file
7
vendor/textalk/websocket/lib/BadUriException.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
class BadUriException extends Exception
|
||||||
|
{
|
||||||
|
}
|
490
vendor/textalk/websocket/lib/Client.php
vendored
Normal file
490
vendor/textalk/websocket/lib/Client.php
vendored
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use ErrorException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Phrity\Net\Uri;
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use Psr\Log\{
|
||||||
|
LoggerAwareInterface,
|
||||||
|
LoggerAwareTrait,
|
||||||
|
LoggerInterface,
|
||||||
|
NullLogger
|
||||||
|
};
|
||||||
|
use WebSocket\Message\Factory;
|
||||||
|
|
||||||
|
class Client implements LoggerAwareInterface
|
||||||
|
{
|
||||||
|
use LoggerAwareTrait; // provides setLogger(LoggerInterface $logger)
|
||||||
|
use OpcodeTrait;
|
||||||
|
|
||||||
|
// Default options
|
||||||
|
protected static $default_options = [
|
||||||
|
'context' => null,
|
||||||
|
'filter' => ['text', 'binary'],
|
||||||
|
'fragment_size' => 4096,
|
||||||
|
'headers' => null,
|
||||||
|
'logger' => null,
|
||||||
|
'origin' => null, // @deprecated
|
||||||
|
'persistent' => false,
|
||||||
|
'return_obj' => false,
|
||||||
|
'timeout' => 5,
|
||||||
|
];
|
||||||
|
|
||||||
|
private $socket_uri;
|
||||||
|
private $connection;
|
||||||
|
private $options = [];
|
||||||
|
private $listen = false;
|
||||||
|
private $last_opcode = null;
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Magic methods ------------------------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UriInterface|string $uri A ws/wss-URI
|
||||||
|
* @param array $options
|
||||||
|
* Associative array containing:
|
||||||
|
* - context: Set the stream context. Default: empty context
|
||||||
|
* - timeout: Set the socket timeout in seconds. Default: 5
|
||||||
|
* - fragment_size: Set framgemnt size. Default: 4096
|
||||||
|
* - headers: Associative array of headers to set/override.
|
||||||
|
*/
|
||||||
|
public function __construct($uri, array $options = [])
|
||||||
|
{
|
||||||
|
$this->socket_uri = $this->parseUri($uri);
|
||||||
|
$this->options = array_merge(self::$default_options, [
|
||||||
|
'logger' => new NullLogger(),
|
||||||
|
], $options);
|
||||||
|
$this->setLogger($this->options['logger']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string representation of instance.
|
||||||
|
* @return string String representation.
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
"%s(%s)",
|
||||||
|
get_class($this),
|
||||||
|
$this->getName() ?: 'closed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Client option functions -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set timeout.
|
||||||
|
* @param int $timeout Timeout in seconds.
|
||||||
|
*/
|
||||||
|
public function setTimeout(int $timeout): void
|
||||||
|
{
|
||||||
|
$this->options['timeout'] = $timeout;
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->connection->setTimeout($timeout);
|
||||||
|
$this->connection->setOptions($this->options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fragmentation size.
|
||||||
|
* @param int $fragment_size Fragment size in bytes.
|
||||||
|
* @return self.
|
||||||
|
*/
|
||||||
|
public function setFragmentSize(int $fragment_size): self
|
||||||
|
{
|
||||||
|
$this->options['fragment_size'] = $fragment_size;
|
||||||
|
$this->connection->setOptions($this->options);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get fragmentation size.
|
||||||
|
* @return int $fragment_size Fragment size in bytes.
|
||||||
|
*/
|
||||||
|
public function getFragmentSize(): int
|
||||||
|
{
|
||||||
|
return $this->options['fragment_size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Connection operations ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send text message.
|
||||||
|
* @param string $payload Content as string.
|
||||||
|
*/
|
||||||
|
public function text(string $payload): void
|
||||||
|
{
|
||||||
|
$this->send($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send binary message.
|
||||||
|
* @param string $payload Content as binary string.
|
||||||
|
*/
|
||||||
|
public function binary(string $payload): void
|
||||||
|
{
|
||||||
|
$this->send($payload, 'binary');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send ping.
|
||||||
|
* @param string $payload Optional text as string.
|
||||||
|
*/
|
||||||
|
public function ping(string $payload = ''): void
|
||||||
|
{
|
||||||
|
$this->send($payload, 'ping');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send unsolicited pong.
|
||||||
|
* @param string $payload Optional text as string.
|
||||||
|
*/
|
||||||
|
public function pong(string $payload = ''): void
|
||||||
|
{
|
||||||
|
$this->send($payload, 'pong');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send message.
|
||||||
|
* @param string $payload Message to send.
|
||||||
|
* @param string $opcode Opcode to use, default: 'text'.
|
||||||
|
* @param bool $masked If message should be masked default: true.
|
||||||
|
*/
|
||||||
|
public function send(string $payload, string $opcode = 'text', bool $masked = true): void
|
||||||
|
{
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($opcode, array_keys(self::$opcodes))) {
|
||||||
|
$warning = "Bad opcode '{$opcode}'. Try 'text' or 'binary'.";
|
||||||
|
$this->logger->warning($warning);
|
||||||
|
throw new BadOpcodeException($warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
$factory = new Factory();
|
||||||
|
$message = $factory->create($opcode, $payload);
|
||||||
|
$this->connection->pushMessage($message, $masked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the socket to close.
|
||||||
|
* @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4
|
||||||
|
* @param string $message A closing message, max 125 bytes.
|
||||||
|
*/
|
||||||
|
public function close(int $status = 1000, string $message = 'ttfn'): void
|
||||||
|
{
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->connection->close($status, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from server.
|
||||||
|
*/
|
||||||
|
public function disconnect(): void
|
||||||
|
{
|
||||||
|
if ($this->isConnected()) {
|
||||||
|
$this->connection->disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive message.
|
||||||
|
* Note that this operation will block reading.
|
||||||
|
* @return mixed Message, text or null depending on settings.
|
||||||
|
*/
|
||||||
|
public function receive()
|
||||||
|
{
|
||||||
|
$filter = $this->options['filter'];
|
||||||
|
$return_obj = $this->options['return_obj'];
|
||||||
|
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$message = $this->connection->pullMessage();
|
||||||
|
$opcode = $message->getOpcode();
|
||||||
|
if (in_array($opcode, $filter)) {
|
||||||
|
$this->last_opcode = $opcode;
|
||||||
|
$return = $return_obj ? $message : $message->getContent();
|
||||||
|
break;
|
||||||
|
} elseif ($opcode == 'close') {
|
||||||
|
$this->last_opcode = null;
|
||||||
|
$return = $return_obj ? $message : null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Connection functions ----------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last received opcode.
|
||||||
|
* @return string|null Opcode.
|
||||||
|
*/
|
||||||
|
public function getLastOpcode(): ?string
|
||||||
|
{
|
||||||
|
return $this->last_opcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get close status on connection.
|
||||||
|
* @return int|null Close status.
|
||||||
|
*/
|
||||||
|
public function getCloseStatus(): ?int
|
||||||
|
{
|
||||||
|
return $this->connection ? $this->connection->getCloseStatus() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If Client has active connection.
|
||||||
|
* @return bool True if active connection.
|
||||||
|
*/
|
||||||
|
public function isConnected(): bool
|
||||||
|
{
|
||||||
|
return $this->connection && $this->connection->isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of local socket, or null if not connected.
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->isConnected() ? $this->connection->getName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of remote socket, or null if not connected.
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getRemoteName(): ?string
|
||||||
|
{
|
||||||
|
return $this->isConnected() ? $this->connection->getRemoteName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of remote socket, or null if not connected.
|
||||||
|
* @return string|null
|
||||||
|
* @deprecated Will be removed in future version, use getPeer() instead.
|
||||||
|
*/
|
||||||
|
public function getPier(): ?string
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'getPier() is deprecated and will be removed in future version. Use getRemoteName() instead.',
|
||||||
|
E_USER_DEPRECATED
|
||||||
|
);
|
||||||
|
return $this->getRemoteName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Helper functions --------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform WebSocket handshake
|
||||||
|
*/
|
||||||
|
protected function connect(): void
|
||||||
|
{
|
||||||
|
$this->connection = null;
|
||||||
|
|
||||||
|
$host_uri = $this->socket_uri
|
||||||
|
->withScheme($this->socket_uri->getScheme() == 'wss' ? 'ssl' : 'tcp')
|
||||||
|
->withPort($this->socket_uri->getPort() ?? ($this->socket_uri->getScheme() == 'wss' ? 443 : 80))
|
||||||
|
->withPath('')
|
||||||
|
->withQuery('')
|
||||||
|
->withFragment('')
|
||||||
|
->withUserInfo('');
|
||||||
|
|
||||||
|
// Path must be absolute
|
||||||
|
$http_path = $this->socket_uri->getPath();
|
||||||
|
if ($http_path === '' || $http_path[0] !== '/') {
|
||||||
|
$http_path = "/{$http_path}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$http_uri = (new Uri())
|
||||||
|
->withPath($http_path)
|
||||||
|
->withQuery($this->socket_uri->getQuery());
|
||||||
|
|
||||||
|
// Set the stream context options if they're already set in the config
|
||||||
|
if (isset($this->options['context'])) {
|
||||||
|
// Suppress the error since we'll catch it below
|
||||||
|
if (@get_resource_type($this->options['context']) === 'stream-context') {
|
||||||
|
$context = $this->options['context'];
|
||||||
|
} else {
|
||||||
|
$error = "Stream context in \$options['context'] isn't a valid context.";
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new \InvalidArgumentException($error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$context = stream_context_create();
|
||||||
|
}
|
||||||
|
|
||||||
|
$persistent = $this->options['persistent'] === true;
|
||||||
|
$flags = STREAM_CLIENT_CONNECT;
|
||||||
|
$flags = $persistent ? $flags | STREAM_CLIENT_PERSISTENT : $flags;
|
||||||
|
$socket = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$socket = $handler->with(function () use ($host_uri, $flags, $context) {
|
||||||
|
$error = $errno = $errstr = null;
|
||||||
|
// Open the socket.
|
||||||
|
return stream_socket_client(
|
||||||
|
$host_uri,
|
||||||
|
$errno,
|
||||||
|
$errstr,
|
||||||
|
$this->options['timeout'],
|
||||||
|
$flags,
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (!$socket) {
|
||||||
|
throw new ErrorException('No socket');
|
||||||
|
}
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$error = "Could not open socket to \"{$host_uri->getAuthority()}\": {$e->getMessage()} ({$e->getCode()}).";
|
||||||
|
$this->logger->error($error, ['severity' => $e->getSeverity()]);
|
||||||
|
throw new ConnectionException($error, 0, [], $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connection = new Connection($socket, $this->options);
|
||||||
|
$this->connection->setLogger($this->logger);
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
$error = "Invalid stream on \"{$host_uri->getAuthority()}\".";
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new ConnectionException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$persistent || $this->connection->tell() == 0) {
|
||||||
|
// Set timeout on the stream as well.
|
||||||
|
$this->connection->setTimeout($this->options['timeout']);
|
||||||
|
|
||||||
|
// Generate the WebSocket key.
|
||||||
|
$key = self::generateKey();
|
||||||
|
|
||||||
|
// Default headers
|
||||||
|
$headers = [
|
||||||
|
'Host' => $host_uri->getAuthority(),
|
||||||
|
'User-Agent' => 'websocket-client-php',
|
||||||
|
'Connection' => 'Upgrade',
|
||||||
|
'Upgrade' => 'websocket',
|
||||||
|
'Sec-WebSocket-Key' => $key,
|
||||||
|
'Sec-WebSocket-Version' => '13',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Handle basic authentication.
|
||||||
|
if ($userinfo = $this->socket_uri->getUserInfo()) {
|
||||||
|
$headers['authorization'] = 'Basic ' . base64_encode($userinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated way of adding origin (use headers instead).
|
||||||
|
if (isset($this->options['origin'])) {
|
||||||
|
$headers['origin'] = $this->options['origin'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add and override with headers from options.
|
||||||
|
if (isset($this->options['headers'])) {
|
||||||
|
$headers = array_merge($headers, $this->options['headers']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = "GET {$http_uri} HTTP/1.1\r\n" . implode(
|
||||||
|
"\r\n",
|
||||||
|
array_map(
|
||||||
|
function ($key, $value) {
|
||||||
|
return "$key: $value";
|
||||||
|
},
|
||||||
|
array_keys($headers),
|
||||||
|
$headers
|
||||||
|
)
|
||||||
|
) . "\r\n\r\n";
|
||||||
|
|
||||||
|
// Send headers.
|
||||||
|
$this->connection->write($header);
|
||||||
|
|
||||||
|
// Get server response header (terminated with double CR+LF).
|
||||||
|
$response = '';
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
$buffer = $this->connection->gets(1024);
|
||||||
|
$response .= $buffer;
|
||||||
|
} while (substr_count($response, "\r\n\r\n") == 0);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new ConnectionException('Client handshake error', $e->getCode(), $e->getData(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate response.
|
||||||
|
if (!preg_match('#Sec-WebSocket-Accept:\s(.*)$#mUi', $response, $matches)) {
|
||||||
|
$error = sprintf(
|
||||||
|
"Connection to '%s' failed: Server sent invalid upgrade response: %s",
|
||||||
|
(string)$this->socket_uri,
|
||||||
|
(string)$response
|
||||||
|
);
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new ConnectionException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyAccept = trim($matches[1]);
|
||||||
|
$expectedResonse = base64_encode(
|
||||||
|
pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($keyAccept !== $expectedResonse) {
|
||||||
|
$error = 'Server sent bad upgrade response.';
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new ConnectionException($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->info("Client connected to {$this->socket_uri}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random string for WebSocket key.
|
||||||
|
* @return string Random string
|
||||||
|
*/
|
||||||
|
protected static function generateKey(): string
|
||||||
|
{
|
||||||
|
$key = '';
|
||||||
|
for ($i = 0; $i < 16; $i++) {
|
||||||
|
$key .= chr(rand(33, 126));
|
||||||
|
}
|
||||||
|
return base64_encode($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseUri($uri): UriInterface
|
||||||
|
{
|
||||||
|
if ($uri instanceof UriInterface) {
|
||||||
|
$uri = $uri;
|
||||||
|
} elseif (is_string($uri)) {
|
||||||
|
try {
|
||||||
|
$uri = new Uri($uri);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
throw new BadUriException("Invalid URI '{$uri}' provided.", 0, $e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new BadUriException("Provided URI must be a UriInterface or string.");
|
||||||
|
}
|
||||||
|
if (!in_array($uri->getScheme(), ['ws', 'wss'])) {
|
||||||
|
throw new BadUriException("Invalid URI scheme, must be 'ws' or 'wss'.");
|
||||||
|
}
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
}
|
518
vendor/textalk/websocket/lib/Connection.php
vendored
Normal file
518
vendor/textalk/websocket/lib/Connection.php
vendored
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use Psr\Log\{
|
||||||
|
LoggerAwareInterface,
|
||||||
|
LoggerAwareTrait,
|
||||||
|
LoggerInterface, NullLogger
|
||||||
|
};
|
||||||
|
use WebSocket\Message\{
|
||||||
|
Factory,
|
||||||
|
Message
|
||||||
|
};
|
||||||
|
|
||||||
|
class Connection implements LoggerAwareInterface
|
||||||
|
{
|
||||||
|
use LoggerAwareTrait;
|
||||||
|
use OpcodeTrait;
|
||||||
|
|
||||||
|
private $stream;
|
||||||
|
private $read_buffer;
|
||||||
|
private $msg_factory;
|
||||||
|
private $options = [];
|
||||||
|
|
||||||
|
protected $is_closing = false;
|
||||||
|
protected $close_status = null;
|
||||||
|
|
||||||
|
private $uid;
|
||||||
|
|
||||||
|
/* ---------- Construct & Destruct ----------------------------------------------- */
|
||||||
|
|
||||||
|
public function __construct($stream, array $options = [])
|
||||||
|
{
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->setOptions($options);
|
||||||
|
$this->setLogger(new NullLogger());
|
||||||
|
$this->msg_factory = new Factory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
if ($this->getType() === 'stream') {
|
||||||
|
fclose($this->stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOptions(array $options = []): void
|
||||||
|
{
|
||||||
|
$this->options = array_merge($this->options, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCloseStatus(): ?int
|
||||||
|
{
|
||||||
|
return $this->close_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the socket to close.
|
||||||
|
*
|
||||||
|
* @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4
|
||||||
|
* @param string $message A closing message, max 125 bytes.
|
||||||
|
*/
|
||||||
|
public function close(int $status = 1000, string $message = 'ttfn'): void
|
||||||
|
{
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$status_binstr = sprintf('%016b', $status);
|
||||||
|
$status_str = '';
|
||||||
|
foreach (str_split($status_binstr, 8) as $binstr) {
|
||||||
|
$status_str .= chr(bindec($binstr));
|
||||||
|
}
|
||||||
|
$message = $this->msg_factory->create('close', $status_str . $message);
|
||||||
|
$this->pushMessage($message, true);
|
||||||
|
|
||||||
|
$this->logger->debug("Closing with status: {$status}.");
|
||||||
|
|
||||||
|
$this->is_closing = true;
|
||||||
|
while (true) {
|
||||||
|
$message = $this->pullMessage();
|
||||||
|
if ($message->getOpcode() == 'close') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Message methods ---------------------------------------------------- */
|
||||||
|
|
||||||
|
// Push a message to stream
|
||||||
|
public function pushMessage(Message $message, bool $masked = true): void
|
||||||
|
{
|
||||||
|
$frames = $message->getFrames($masked, $this->options['fragment_size']);
|
||||||
|
foreach ($frames as $frame) {
|
||||||
|
$this->pushFrame($frame);
|
||||||
|
}
|
||||||
|
$this->logger->info("[connection] Pushed {$message}", [
|
||||||
|
'opcode' => $message->getOpcode(),
|
||||||
|
'content-length' => $message->getLength(),
|
||||||
|
'frames' => count($frames),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull a message from stream
|
||||||
|
public function pullMessage(): Message
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
$frame = $this->pullFrame();
|
||||||
|
$frame = $this->autoRespond($frame);
|
||||||
|
list ($final, $payload, $opcode, $masked) = $frame;
|
||||||
|
|
||||||
|
if ($opcode == 'close') {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continuation and factual opcode
|
||||||
|
$continuation = $opcode == 'continuation';
|
||||||
|
$payload_opcode = $continuation ? $this->read_buffer['opcode'] : $opcode;
|
||||||
|
|
||||||
|
// First continuation frame, create buffer
|
||||||
|
if (!$final && !$continuation) {
|
||||||
|
$this->read_buffer = ['opcode' => $opcode, 'payload' => $payload, 'frames' => 1];
|
||||||
|
continue; // Continue reading
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subsequent continuation frames, add to buffer
|
||||||
|
if ($continuation) {
|
||||||
|
$this->read_buffer['payload'] .= $payload;
|
||||||
|
$this->read_buffer['frames']++;
|
||||||
|
}
|
||||||
|
} while (!$final);
|
||||||
|
|
||||||
|
// Final, return payload
|
||||||
|
$frames = 1;
|
||||||
|
if ($continuation) {
|
||||||
|
$payload = $this->read_buffer['payload'];
|
||||||
|
$frames = $this->read_buffer['frames'];
|
||||||
|
$this->read_buffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = $this->msg_factory->create($payload_opcode, $payload);
|
||||||
|
|
||||||
|
$this->logger->info("[connection] Pulled {$message}", [
|
||||||
|
'opcode' => $payload_opcode,
|
||||||
|
'content-length' => strlen($payload),
|
||||||
|
'frames' => $frames,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Frame I/O methods -------------------------------------------------- */
|
||||||
|
|
||||||
|
// Pull frame from stream
|
||||||
|
private function pullFrame(): array
|
||||||
|
{
|
||||||
|
// Read the fragment "header" first, two bytes.
|
||||||
|
$data = $this->read(2);
|
||||||
|
list ($byte_1, $byte_2) = array_values(unpack('C*', $data));
|
||||||
|
$final = (bool)($byte_1 & 0b10000000); // Final fragment marker.
|
||||||
|
$rsv = $byte_1 & 0b01110000; // Unused bits, ignore
|
||||||
|
|
||||||
|
// Parse opcode
|
||||||
|
$opcode_int = $byte_1 & 0b00001111;
|
||||||
|
$opcode_ints = array_flip(self::$opcodes);
|
||||||
|
if (!array_key_exists($opcode_int, $opcode_ints)) {
|
||||||
|
$warning = "Bad opcode in websocket frame: {$opcode_int}";
|
||||||
|
$this->logger->warning($warning);
|
||||||
|
throw new ConnectionException($warning, ConnectionException::BAD_OPCODE);
|
||||||
|
}
|
||||||
|
$opcode = $opcode_ints[$opcode_int];
|
||||||
|
|
||||||
|
// Masking bit
|
||||||
|
$masked = (bool)($byte_2 & 0b10000000);
|
||||||
|
|
||||||
|
$payload = '';
|
||||||
|
|
||||||
|
// Payload length
|
||||||
|
$payload_length = $byte_2 & 0b01111111;
|
||||||
|
|
||||||
|
if ($payload_length > 125) {
|
||||||
|
if ($payload_length === 126) {
|
||||||
|
$data = $this->read(2); // 126: Payload is a 16-bit unsigned int
|
||||||
|
$payload_length = current(unpack('n', $data));
|
||||||
|
} else {
|
||||||
|
$data = $this->read(8); // 127: Payload is a 64-bit unsigned int
|
||||||
|
$payload_length = current(unpack('J', $data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get masking key.
|
||||||
|
if ($masked) {
|
||||||
|
$masking_key = $this->read(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual payload, if any (might not be for e.g. close frames.
|
||||||
|
if ($payload_length > 0) {
|
||||||
|
$data = $this->read($payload_length);
|
||||||
|
|
||||||
|
if ($masked) {
|
||||||
|
// Unmask payload.
|
||||||
|
for ($i = 0; $i < $payload_length; $i++) {
|
||||||
|
$payload .= ($data[$i] ^ $masking_key[$i % 4]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$payload = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->debug("[connection] Pulled '{opcode}' frame", [
|
||||||
|
'opcode' => $opcode,
|
||||||
|
'final' => $final,
|
||||||
|
'content-length' => strlen($payload),
|
||||||
|
]);
|
||||||
|
return [$final, $payload, $opcode, $masked];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push frame to stream
|
||||||
|
private function pushFrame(array $frame): void
|
||||||
|
{
|
||||||
|
list ($final, $payload, $opcode, $masked) = $frame;
|
||||||
|
$data = '';
|
||||||
|
$byte_1 = $final ? 0b10000000 : 0b00000000; // Final fragment marker.
|
||||||
|
$byte_1 |= self::$opcodes[$opcode]; // Set opcode.
|
||||||
|
$data .= pack('C', $byte_1);
|
||||||
|
|
||||||
|
$byte_2 = $masked ? 0b10000000 : 0b00000000; // Masking bit marker.
|
||||||
|
|
||||||
|
// 7 bits of payload length...
|
||||||
|
$payload_length = strlen($payload);
|
||||||
|
if ($payload_length > 65535) {
|
||||||
|
$data .= pack('C', $byte_2 | 0b01111111);
|
||||||
|
$data .= pack('J', $payload_length);
|
||||||
|
} elseif ($payload_length > 125) {
|
||||||
|
$data .= pack('C', $byte_2 | 0b01111110);
|
||||||
|
$data .= pack('n', $payload_length);
|
||||||
|
} else {
|
||||||
|
$data .= pack('C', $byte_2 | $payload_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle masking
|
||||||
|
if ($masked) {
|
||||||
|
// generate a random mask:
|
||||||
|
$mask = '';
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$mask .= chr(rand(0, 255));
|
||||||
|
}
|
||||||
|
$data .= $mask;
|
||||||
|
|
||||||
|
// Append payload to frame:
|
||||||
|
for ($i = 0; $i < $payload_length; $i++) {
|
||||||
|
$data .= $payload[$i] ^ $mask[$i % 4];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$data .= $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->write($data);
|
||||||
|
|
||||||
|
$this->logger->debug("[connection] Pushed '{$opcode}' frame", [
|
||||||
|
'opcode' => $opcode,
|
||||||
|
'final' => $final,
|
||||||
|
'content-length' => strlen($payload),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger auto response for frame
|
||||||
|
private function autoRespond(array $frame)
|
||||||
|
{
|
||||||
|
list ($final, $payload, $opcode, $masked) = $frame;
|
||||||
|
$payload_length = strlen($payload);
|
||||||
|
|
||||||
|
switch ($opcode) {
|
||||||
|
case 'ping':
|
||||||
|
// If we received a ping, respond with a pong
|
||||||
|
$this->logger->debug("[connection] Received 'ping', sending 'pong'.");
|
||||||
|
$message = $this->msg_factory->create('pong', $payload);
|
||||||
|
$this->pushMessage($message, $masked);
|
||||||
|
return [$final, $payload, $opcode, $masked];
|
||||||
|
case 'close':
|
||||||
|
// If we received close, possibly acknowledge and close connection
|
||||||
|
$status_bin = '';
|
||||||
|
$status = '';
|
||||||
|
if ($payload_length > 0) {
|
||||||
|
$status_bin = $payload[0] . $payload[1];
|
||||||
|
$status = current(unpack('n', $payload));
|
||||||
|
$this->close_status = $status;
|
||||||
|
}
|
||||||
|
// Get additional close message
|
||||||
|
if ($payload_length >= 2) {
|
||||||
|
$payload = substr($payload, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->debug("[connection] Received 'close', status: {$status}.");
|
||||||
|
if (!$this->is_closing) {
|
||||||
|
$ack = "{$status_bin}Close acknowledged: {$status}";
|
||||||
|
$message = $this->msg_factory->create('close', $ack);
|
||||||
|
$this->pushMessage($message, $masked);
|
||||||
|
} else {
|
||||||
|
$this->is_closing = false; // A close response, all done.
|
||||||
|
}
|
||||||
|
$this->disconnect();
|
||||||
|
return [$final, $payload, $opcode, $masked];
|
||||||
|
default:
|
||||||
|
return [$final, $payload, $opcode, $masked];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Stream I/O methods ------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close connection stream.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function disconnect(): bool
|
||||||
|
{
|
||||||
|
$this->logger->debug('Closing connection');
|
||||||
|
return fclose($this->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If connected to stream.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isConnected(): bool
|
||||||
|
{
|
||||||
|
return in_array($this->getType(), ['stream', 'persistent stream']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return type of connection.
|
||||||
|
* @return string|null Type of connection or null if invalid type.
|
||||||
|
*/
|
||||||
|
public function getType(): ?string
|
||||||
|
{
|
||||||
|
return get_resource_type($this->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of local socket, or null if not connected.
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return stream_socket_get_name($this->stream, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of remote socket, or null if not connected.
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getRemoteName(): ?string
|
||||||
|
{
|
||||||
|
return stream_socket_get_name($this->stream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get meta data for connection.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getMeta(): array
|
||||||
|
{
|
||||||
|
return stream_get_meta_data($this->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current position of stream pointer.
|
||||||
|
* @return int
|
||||||
|
* @throws ConnectionException
|
||||||
|
*/
|
||||||
|
public function tell(): int
|
||||||
|
{
|
||||||
|
$tell = ftell($this->stream);
|
||||||
|
if ($tell === false) {
|
||||||
|
$this->throwException('Could not resolve stream pointer position');
|
||||||
|
}
|
||||||
|
return $tell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If stream pointer is at end of file.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function eof(): int
|
||||||
|
{
|
||||||
|
return feof($this->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Stream option methods ---------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set time out on connection.
|
||||||
|
* @param int $seconds Timeout part in seconds
|
||||||
|
* @param int $microseconds Timeout part in microseconds
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setTimeout(int $seconds, int $microseconds = 0): bool
|
||||||
|
{
|
||||||
|
$this->logger->debug("Setting timeout {$seconds}:{$microseconds} seconds");
|
||||||
|
return stream_set_timeout($this->stream, $seconds, $microseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Stream read/write methods ------------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read line from stream.
|
||||||
|
* @param int $length Maximum number of bytes to read
|
||||||
|
* @param string $ending Line delimiter
|
||||||
|
* @return string Read data
|
||||||
|
*/
|
||||||
|
public function getLine(int $length, string $ending): string
|
||||||
|
{
|
||||||
|
$line = stream_get_line($this->stream, $length, $ending);
|
||||||
|
if ($line === false) {
|
||||||
|
$this->throwException('Could not read from stream');
|
||||||
|
}
|
||||||
|
$read = strlen($line);
|
||||||
|
$this->logger->debug("Read {$read} bytes of line.");
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read line from stream.
|
||||||
|
* @param int $length Maximum number of bytes to read
|
||||||
|
* @return string Read data
|
||||||
|
*/
|
||||||
|
public function gets(int $length): string
|
||||||
|
{
|
||||||
|
$line = fgets($this->stream, $length);
|
||||||
|
if ($line === false) {
|
||||||
|
$this->throwException('Could not read from stream');
|
||||||
|
}
|
||||||
|
$read = strlen($line);
|
||||||
|
$this->logger->debug("Read {$read} bytes of line.");
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read characters from stream.
|
||||||
|
* @param int $length Maximum number of bytes to read
|
||||||
|
* @return string Read data
|
||||||
|
*/
|
||||||
|
public function read(string $length): string
|
||||||
|
{
|
||||||
|
$data = '';
|
||||||
|
while (strlen($data) < $length) {
|
||||||
|
$buffer = fread($this->stream, $length - strlen($data));
|
||||||
|
if (!$buffer) {
|
||||||
|
$meta = stream_get_meta_data($this->stream);
|
||||||
|
if (!empty($meta['timed_out'])) {
|
||||||
|
$message = 'Client read timeout';
|
||||||
|
$this->logger->error($message, $meta);
|
||||||
|
throw new TimeoutException($message, ConnectionException::TIMED_OUT, $meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($buffer === false) {
|
||||||
|
$read = strlen($data);
|
||||||
|
$this->throwException("Broken frame, read {$read} of stated {$length} bytes.");
|
||||||
|
}
|
||||||
|
if ($buffer === '') {
|
||||||
|
$this->throwException("Empty read; connection dead?");
|
||||||
|
}
|
||||||
|
$data .= $buffer;
|
||||||
|
$read = strlen($data);
|
||||||
|
$this->logger->debug("Read {$read} of {$length} bytes.");
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write characters to stream.
|
||||||
|
* @param string $data Data to read
|
||||||
|
*/
|
||||||
|
public function write(string $data): void
|
||||||
|
{
|
||||||
|
$length = strlen($data);
|
||||||
|
$written = fwrite($this->stream, $data);
|
||||||
|
if ($written === false) {
|
||||||
|
$this->throwException("Failed to write {$length} bytes.");
|
||||||
|
}
|
||||||
|
if ($written < strlen($data)) {
|
||||||
|
$this->throwException("Could only write {$written} out of {$length} bytes.");
|
||||||
|
}
|
||||||
|
$this->logger->debug("Wrote {$written} of {$length} bytes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Internal helper methods -------------------------------------------- */
|
||||||
|
|
||||||
|
private function throwException(string $message, int $code = 0): void
|
||||||
|
{
|
||||||
|
$meta = ['closed' => true];
|
||||||
|
if ($this->isConnected()) {
|
||||||
|
$meta = $this->getMeta();
|
||||||
|
$this->disconnect();
|
||||||
|
if (!empty($meta['timed_out'])) {
|
||||||
|
$this->logger->error($message, $meta);
|
||||||
|
throw new TimeoutException($message, ConnectionException::TIMED_OUT, $meta);
|
||||||
|
}
|
||||||
|
if (!empty($meta['eof'])) {
|
||||||
|
$code = ConnectionException::EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->logger->error($message, $meta);
|
||||||
|
throw new ConnectionException($message, $code, $meta);
|
||||||
|
}
|
||||||
|
}
|
33
vendor/textalk/websocket/lib/ConnectionException.php
vendored
Normal file
33
vendor/textalk/websocket/lib/ConnectionException.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ConnectionException extends Exception
|
||||||
|
{
|
||||||
|
// Native codes in interval 0-106
|
||||||
|
public const TIMED_OUT = 1024;
|
||||||
|
public const EOF = 1025;
|
||||||
|
public const BAD_OPCODE = 1026;
|
||||||
|
|
||||||
|
private $data;
|
||||||
|
|
||||||
|
public function __construct(string $message, int $code = 0, array $data = [], Throwable $prev = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $prev);
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
}
|
7
vendor/textalk/websocket/lib/Exception.php
vendored
Normal file
7
vendor/textalk/websocket/lib/Exception.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
class Exception extends \Exception
|
||||||
|
{
|
||||||
|
}
|
15
vendor/textalk/websocket/lib/Message/Binary.php
vendored
Normal file
15
vendor/textalk/websocket/lib/Message/Binary.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
class Binary extends Message
|
||||||
|
{
|
||||||
|
protected $opcode = 'binary';
|
||||||
|
}
|
15
vendor/textalk/websocket/lib/Message/Close.php
vendored
Normal file
15
vendor/textalk/websocket/lib/Message/Close.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
class Close extends Message
|
||||||
|
{
|
||||||
|
protected $opcode = 'close';
|
||||||
|
}
|
32
vendor/textalk/websocket/lib/Message/Factory.php
vendored
Normal file
32
vendor/textalk/websocket/lib/Message/Factory.php
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
use WebSocket\BadOpcodeException;
|
||||||
|
|
||||||
|
class Factory
|
||||||
|
{
|
||||||
|
public function create(string $opcode, string $payload = ''): Message
|
||||||
|
{
|
||||||
|
switch ($opcode) {
|
||||||
|
case 'text':
|
||||||
|
return new Text($payload);
|
||||||
|
case 'binary':
|
||||||
|
return new Binary($payload);
|
||||||
|
case 'ping':
|
||||||
|
return new Ping($payload);
|
||||||
|
case 'pong':
|
||||||
|
return new Pong($payload);
|
||||||
|
case 'close':
|
||||||
|
return new Close($payload);
|
||||||
|
}
|
||||||
|
throw new BadOpcodeException("Invalid opcode '{$opcode}' provided");
|
||||||
|
}
|
||||||
|
}
|
74
vendor/textalk/websocket/lib/Message/Message.php
vendored
Normal file
74
vendor/textalk/websocket/lib/Message/Message.php
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
abstract class Message
|
||||||
|
{
|
||||||
|
protected $opcode;
|
||||||
|
protected $payload;
|
||||||
|
protected $timestamp;
|
||||||
|
|
||||||
|
public function __construct(string $payload = '')
|
||||||
|
{
|
||||||
|
$this->payload = $payload;
|
||||||
|
$this->timestamp = new DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOpcode(): string
|
||||||
|
{
|
||||||
|
return $this->opcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLength(): int
|
||||||
|
{
|
||||||
|
return strlen($this->payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimestamp(): DateTime
|
||||||
|
{
|
||||||
|
return $this->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContent(string $payload = ''): void
|
||||||
|
{
|
||||||
|
$this->payload = $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasContent(): bool
|
||||||
|
{
|
||||||
|
return $this->payload != '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return get_class($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split messages into frames
|
||||||
|
public function getFrames(bool $masked = true, int $framesize = 4096): array
|
||||||
|
{
|
||||||
|
|
||||||
|
$frames = [];
|
||||||
|
$split = str_split($this->getContent(), $framesize) ?: [''];
|
||||||
|
foreach ($split as $payload) {
|
||||||
|
$frames[] = [false, $payload, 'continuation', $masked];
|
||||||
|
}
|
||||||
|
$frames[0][2] = $this->opcode;
|
||||||
|
$frames[array_key_last($frames)][0] = true;
|
||||||
|
return $frames;
|
||||||
|
}
|
||||||
|
}
|
15
vendor/textalk/websocket/lib/Message/Ping.php
vendored
Normal file
15
vendor/textalk/websocket/lib/Message/Ping.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
class Ping extends Message
|
||||||
|
{
|
||||||
|
protected $opcode = 'ping';
|
||||||
|
}
|
15
vendor/textalk/websocket/lib/Message/Pong.php
vendored
Normal file
15
vendor/textalk/websocket/lib/Message/Pong.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
class Pong extends Message
|
||||||
|
{
|
||||||
|
protected $opcode = 'pong';
|
||||||
|
}
|
15
vendor/textalk/websocket/lib/Message/Text.php
vendored
Normal file
15
vendor/textalk/websocket/lib/Message/Text.php
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket\Message;
|
||||||
|
|
||||||
|
class Text extends Message
|
||||||
|
{
|
||||||
|
protected $opcode = 'text';
|
||||||
|
}
|
22
vendor/textalk/websocket/lib/OpcodeTrait.php
vendored
Normal file
22
vendor/textalk/websocket/lib/OpcodeTrait.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
trait OpcodeTrait
|
||||||
|
{
|
||||||
|
private static $opcodes = [
|
||||||
|
'continuation' => 0,
|
||||||
|
'text' => 1,
|
||||||
|
'binary' => 2,
|
||||||
|
'close' => 8,
|
||||||
|
'ping' => 9,
|
||||||
|
'pong' => 10,
|
||||||
|
];
|
||||||
|
}
|
470
vendor/textalk/websocket/lib/Server.php
vendored
Normal file
470
vendor/textalk/websocket/lib/Server.php
vendored
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use ErrorException;
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
use Psr\Log\{
|
||||||
|
LoggerAwareInterface,
|
||||||
|
LoggerAwareTrait,
|
||||||
|
LoggerInterface,
|
||||||
|
NullLogger
|
||||||
|
};
|
||||||
|
use Throwable;
|
||||||
|
use WebSocket\Message\Factory;
|
||||||
|
|
||||||
|
class Server implements LoggerAwareInterface
|
||||||
|
{
|
||||||
|
use LoggerAwareTrait; // Provides setLogger(LoggerInterface $logger)
|
||||||
|
use OpcodeTrait;
|
||||||
|
|
||||||
|
// Default options
|
||||||
|
protected static $default_options = [
|
||||||
|
'filter' => ['text', 'binary'],
|
||||||
|
'fragment_size' => 4096,
|
||||||
|
'logger' => null,
|
||||||
|
'port' => 8000,
|
||||||
|
'return_obj' => false,
|
||||||
|
'timeout' => null,
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $port;
|
||||||
|
protected $listening;
|
||||||
|
protected $request;
|
||||||
|
protected $request_path;
|
||||||
|
private $connections = [];
|
||||||
|
private $options = [];
|
||||||
|
private $listen = false;
|
||||||
|
private $last_opcode;
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Magic methods ------------------------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $options
|
||||||
|
* Associative array containing:
|
||||||
|
* - filter: Array of opcodes to handle. Default: ['text', 'binary'].
|
||||||
|
* - fragment_size: Set framgemnt size. Default: 4096
|
||||||
|
* - logger: PSR-3 compatible logger. Default NullLogger.
|
||||||
|
* - port: Chose port for listening. Default 8000.
|
||||||
|
* - return_obj: If receive() function return Message instance. Default false.
|
||||||
|
* - timeout: Set the socket timeout in seconds.
|
||||||
|
*/
|
||||||
|
public function __construct(array $options = [])
|
||||||
|
{
|
||||||
|
$this->options = array_merge(self::$default_options, [
|
||||||
|
'logger' => new NullLogger(),
|
||||||
|
], $options);
|
||||||
|
$this->port = $this->options['port'];
|
||||||
|
$this->setLogger($this->options['logger']);
|
||||||
|
|
||||||
|
$error = $errno = $errstr = null;
|
||||||
|
set_error_handler(function (int $severity, string $message, string $file, int $line) use (&$error) {
|
||||||
|
$this->logger->warning($message, ['severity' => $severity]);
|
||||||
|
$error = $message;
|
||||||
|
}, E_ALL);
|
||||||
|
|
||||||
|
do {
|
||||||
|
$this->listening = stream_socket_server("tcp://0.0.0.0:$this->port", $errno, $errstr);
|
||||||
|
} while ($this->listening === false && $this->port++ < 10000);
|
||||||
|
|
||||||
|
restore_error_handler();
|
||||||
|
|
||||||
|
if (!$this->listening) {
|
||||||
|
$error = "Could not open listening socket: {$errstr} ({$errno}) {$error}";
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new ConnectionException($error, (int)$errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->info("Server listening to port {$this->port}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string representation of instance.
|
||||||
|
* @return string String representation.
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
"%s(%s)",
|
||||||
|
get_class($this),
|
||||||
|
$this->getName() ?: 'closed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Server operations -------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept a single incoming request.
|
||||||
|
* Note that this operation will block accepting additional requests.
|
||||||
|
* @return bool True if listening.
|
||||||
|
*/
|
||||||
|
public function accept(): bool
|
||||||
|
{
|
||||||
|
$this->disconnect();
|
||||||
|
return (bool)$this->listening;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Server option functions -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current port.
|
||||||
|
* @return int port.
|
||||||
|
*/
|
||||||
|
public function getPort(): int
|
||||||
|
{
|
||||||
|
return $this->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set timeout.
|
||||||
|
* @param int $timeout Timeout in seconds.
|
||||||
|
*/
|
||||||
|
public function setTimeout(int $timeout): void
|
||||||
|
{
|
||||||
|
$this->options['timeout'] = $timeout;
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($this->connections as $connection) {
|
||||||
|
$connection->setTimeout($timeout);
|
||||||
|
$connection->setOptions($this->options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fragmentation size.
|
||||||
|
* @param int $fragment_size Fragment size in bytes.
|
||||||
|
* @return self.
|
||||||
|
*/
|
||||||
|
public function setFragmentSize(int $fragment_size): self
|
||||||
|
{
|
||||||
|
$this->options['fragment_size'] = $fragment_size;
|
||||||
|
foreach ($this->connections as $connection) {
|
||||||
|
$connection->setOptions($this->options);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get fragmentation size.
|
||||||
|
* @return int $fragment_size Fragment size in bytes.
|
||||||
|
*/
|
||||||
|
public function getFragmentSize(): int
|
||||||
|
{
|
||||||
|
return $this->options['fragment_size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Connection broadcast operations ------------------------------------ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast text message to all conenctions.
|
||||||
|
* @param string $payload Content as string.
|
||||||
|
*/
|
||||||
|
public function text(string $payload): void
|
||||||
|
{
|
||||||
|
$this->send($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast binary message to all conenctions.
|
||||||
|
* @param string $payload Content as binary string.
|
||||||
|
*/
|
||||||
|
public function binary(string $payload): void
|
||||||
|
{
|
||||||
|
$this->send($payload, 'binary');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast ping message to all conenctions.
|
||||||
|
* @param string $payload Optional text as string.
|
||||||
|
*/
|
||||||
|
public function ping(string $payload = ''): void
|
||||||
|
{
|
||||||
|
$this->send($payload, 'ping');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast pong message to all conenctions.
|
||||||
|
* @param string $payload Optional text as string.
|
||||||
|
*/
|
||||||
|
public function pong(string $payload = ''): void
|
||||||
|
{
|
||||||
|
$this->send($payload, 'pong');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send message on all connections.
|
||||||
|
* @param string $payload Message to send.
|
||||||
|
* @param string $opcode Opcode to use, default: 'text'.
|
||||||
|
* @param bool $masked If message should be masked default: true.
|
||||||
|
*/
|
||||||
|
public function send(string $payload, string $opcode = 'text', bool $masked = true): void
|
||||||
|
{
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
if (!in_array($opcode, array_keys(self::$opcodes))) {
|
||||||
|
$warning = "Bad opcode '{$opcode}'. Try 'text' or 'binary'.";
|
||||||
|
$this->logger->warning($warning);
|
||||||
|
throw new BadOpcodeException($warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
$factory = new Factory();
|
||||||
|
$message = $factory->create($opcode, $payload);
|
||||||
|
|
||||||
|
foreach ($this->connections as $connection) {
|
||||||
|
$connection->pushMessage($message, $masked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all connections.
|
||||||
|
* @param int $status Close status, default: 1000.
|
||||||
|
* @param string $message Close message, default: 'ttfn'.
|
||||||
|
*/
|
||||||
|
public function close(int $status = 1000, string $message = 'ttfn'): void
|
||||||
|
{
|
||||||
|
foreach ($this->connections as $connection) {
|
||||||
|
if ($connection->isConnected()) {
|
||||||
|
$connection->close($status, $message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect all connections.
|
||||||
|
*/
|
||||||
|
public function disconnect(): void
|
||||||
|
{
|
||||||
|
foreach ($this->connections as $connection) {
|
||||||
|
if ($connection->isConnected()) {
|
||||||
|
$connection->disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->connections = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive message from single connection.
|
||||||
|
* Note that this operation will block reading and only read from first available connection.
|
||||||
|
* @return mixed Message, text or null depending on settings.
|
||||||
|
*/
|
||||||
|
public function receive()
|
||||||
|
{
|
||||||
|
$filter = $this->options['filter'];
|
||||||
|
$return_obj = $this->options['return_obj'];
|
||||||
|
|
||||||
|
if (!$this->isConnected()) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
$connection = current($this->connections);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$message = $connection->pullMessage();
|
||||||
|
$opcode = $message->getOpcode();
|
||||||
|
if (in_array($opcode, $filter)) {
|
||||||
|
$this->last_opcode = $opcode;
|
||||||
|
$return = $return_obj ? $message : $message->getContent();
|
||||||
|
break;
|
||||||
|
} elseif ($opcode == 'close') {
|
||||||
|
$this->last_opcode = null;
|
||||||
|
$return = $return_obj ? $message : null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Connection functions ----------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get requested path from last connection.
|
||||||
|
* @return string Path.
|
||||||
|
*/
|
||||||
|
public function getPath(): string
|
||||||
|
{
|
||||||
|
return $this->request_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get request from last connection.
|
||||||
|
* @return array Request.
|
||||||
|
*/
|
||||||
|
public function getRequest(): array
|
||||||
|
{
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get headers from last connection.
|
||||||
|
* @return string|null Headers.
|
||||||
|
*/
|
||||||
|
public function getHeader($header): ?string
|
||||||
|
{
|
||||||
|
foreach ($this->request as $row) {
|
||||||
|
if (stripos($row, $header) !== false) {
|
||||||
|
list($headername, $headervalue) = explode(":", $row);
|
||||||
|
return trim($headervalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last received opcode.
|
||||||
|
* @return string|null Opcode.
|
||||||
|
*/
|
||||||
|
public function getLastOpcode(): ?string
|
||||||
|
{
|
||||||
|
return $this->last_opcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get close status from single connection.
|
||||||
|
* @return int|null Close status.
|
||||||
|
*/
|
||||||
|
public function getCloseStatus(): ?int
|
||||||
|
{
|
||||||
|
return $this->connections ? current($this->connections)->getCloseStatus() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If Server has active connections.
|
||||||
|
* @return bool True if active connection.
|
||||||
|
*/
|
||||||
|
public function isConnected(): bool
|
||||||
|
{
|
||||||
|
foreach ($this->connections as $connection) {
|
||||||
|
if ($connection->isConnected()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of local socket from single connection.
|
||||||
|
* @return string|null Name of local socket.
|
||||||
|
*/
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->isConnected() ? current($this->connections)->getName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of remote socket from single connection.
|
||||||
|
* @return string|null Name of remote socket.
|
||||||
|
*/
|
||||||
|
public function getRemoteName(): ?string
|
||||||
|
{
|
||||||
|
return $this->isConnected() ? current($this->connections)->getRemoteName() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Will be removed in future version.
|
||||||
|
*/
|
||||||
|
public function getPier(): ?string
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'getPier() is deprecated and will be removed in future version. Use getRemoteName() instead.',
|
||||||
|
E_USER_DEPRECATED
|
||||||
|
);
|
||||||
|
return $this->getRemoteName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Helper functions --------------------------------------------------- */
|
||||||
|
|
||||||
|
// Connect when read/write operation is performed.
|
||||||
|
private function connect(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$handler = new ErrorHandler();
|
||||||
|
$socket = $handler->with(function () {
|
||||||
|
if (isset($this->options['timeout'])) {
|
||||||
|
$socket = stream_socket_accept($this->listening, $this->options['timeout']);
|
||||||
|
} else {
|
||||||
|
$socket = stream_socket_accept($this->listening);
|
||||||
|
}
|
||||||
|
if (!$socket) {
|
||||||
|
throw new ErrorException('No socket');
|
||||||
|
}
|
||||||
|
return $socket;
|
||||||
|
});
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$error = "Server failed to connect. {$e->getMessage()}";
|
||||||
|
$this->logger->error($error, ['severity' => $e->getSeverity()]);
|
||||||
|
throw new ConnectionException($error, 0, [], $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$connection = new Connection($socket, $this->options);
|
||||||
|
$connection->setLogger($this->logger);
|
||||||
|
|
||||||
|
if (isset($this->options['timeout'])) {
|
||||||
|
$connection->setTimeout($this->options['timeout']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->info("Client has connected to port {port}", [
|
||||||
|
'port' => $this->port,
|
||||||
|
'peer' => $connection->getRemoteName(),
|
||||||
|
]);
|
||||||
|
$this->performHandshake($connection);
|
||||||
|
$this->connections = ['*' => $connection];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform upgrade handshake on new connections.
|
||||||
|
private function performHandshake(Connection $connection): void
|
||||||
|
{
|
||||||
|
$request = '';
|
||||||
|
do {
|
||||||
|
$buffer = $connection->getLine(1024, "\r\n");
|
||||||
|
$request .= $buffer . "\n";
|
||||||
|
$metadata = $connection->getMeta();
|
||||||
|
} while (!$connection->eof() && $metadata['unread_bytes'] > 0);
|
||||||
|
|
||||||
|
if (!preg_match('/GET (.*) HTTP\//mUi', $request, $matches)) {
|
||||||
|
$error = "No GET in request: {$request}";
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new ConnectionException($error);
|
||||||
|
}
|
||||||
|
$get_uri = trim($matches[1]);
|
||||||
|
$uri_parts = parse_url($get_uri);
|
||||||
|
|
||||||
|
$this->request = explode("\n", $request);
|
||||||
|
$this->request_path = $uri_parts['path'];
|
||||||
|
/// @todo Get query and fragment as well.
|
||||||
|
|
||||||
|
if (!preg_match('#Sec-WebSocket-Key:\s(.*)$#mUi', $request, $matches)) {
|
||||||
|
$error = "Client had no Key in upgrade request: {$request}";
|
||||||
|
$this->logger->error($error);
|
||||||
|
throw new ConnectionException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = trim($matches[1]);
|
||||||
|
|
||||||
|
/// @todo Validate key length and base 64...
|
||||||
|
$response_key = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
|
||||||
|
|
||||||
|
$header = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
. "Upgrade: websocket\r\n"
|
||||||
|
. "Connection: Upgrade\r\n"
|
||||||
|
. "Sec-WebSocket-Accept: $response_key\r\n"
|
||||||
|
. "\r\n";
|
||||||
|
|
||||||
|
$connection->write($header);
|
||||||
|
$this->logger->debug("Handshake on {$get_uri}");
|
||||||
|
}
|
||||||
|
}
|
14
vendor/textalk/websocket/lib/TimeoutException.php
vendored
Normal file
14
vendor/textalk/websocket/lib/TimeoutException.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2022 Textalk/Abicart and contributors.
|
||||||
|
*
|
||||||
|
* This file is part of Websocket PHP and is free software under the ISC License.
|
||||||
|
* License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
class TimeoutException extends ConnectionException
|
||||||
|
{
|
||||||
|
}
|
13
vendor/textalk/websocket/phpunit.xml.dist
vendored
Normal file
13
vendor/textalk/websocket/phpunit.xml.dist
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php" colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||||
|
<coverage>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">lib/</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Unit tests">
|
||||||
|
<directory suffix=".php">tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
568
vendor/textalk/websocket/tests/ClientTest.php
vendored
Normal file
568
vendor/textalk/websocket/tests/ClientTest.php
vendored
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for Client.
|
||||||
|
* Note that this test is performed by mocking socket/stream calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use ErrorException;
|
||||||
|
use Phrity\Net\Uri;
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ClientTest extends TestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
error_reporting(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientMasked(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertEquals(4096, $client->getFragmentSize());
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive', $this);
|
||||||
|
$client->send('Sending a message');
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertEquals('text', $client->getLastOpcode());
|
||||||
|
|
||||||
|
MockSocket::initialize('client.close', $this);
|
||||||
|
$this->assertTrue($client->isConnected());
|
||||||
|
$this->assertNull($client->getCloseStatus());
|
||||||
|
|
||||||
|
$client->close();
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$this->assertEquals(1000, $client->getCloseStatus());
|
||||||
|
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDestruct(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('client.destruct', $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClienExtendedUrl(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-extended', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path?my_query=yes#my_fragment');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientNoPath(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-root', $this);
|
||||||
|
$client = new Client('ws://localhost:8000');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientRelativePath(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$uri = new Uri('ws://localhost:8000');
|
||||||
|
$uri = $uri->withPath('my/mock/path');
|
||||||
|
$client = new Client($uri);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientWsDefaultPort(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-default-port-ws', $this);
|
||||||
|
$uri = new Uri('ws://localhost');
|
||||||
|
$uri = $uri->withPath('my/mock/path');
|
||||||
|
$client = new Client($uri);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientWssDefaultPort(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-default-port-wss', $this);
|
||||||
|
$uri = new Uri('wss://localhost');
|
||||||
|
$uri = $uri->withPath('my/mock/path');
|
||||||
|
$client = new Client($uri);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientWithTimeout(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-timeout', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path', ['timeout' => 300]);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientWithContext(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-context', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path', ['context' => '@mock-stream-context']);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClientAuthed(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-authed', $this);
|
||||||
|
$client = new Client('wss://usename:password@localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithHeaders(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-headers', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path', [
|
||||||
|
'origin' => 'Origin header',
|
||||||
|
'headers' => ['Generic header' => 'Generic content'],
|
||||||
|
]);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPayload128(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
$payload = file_get_contents(__DIR__ . '/mock/payload.128.txt');
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive-128', $this);
|
||||||
|
$client->send($payload, 'text', false);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals($payload, $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPayload65536(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
$payload = file_get_contents(__DIR__ . '/mock/payload.65536.txt');
|
||||||
|
$client->setFragmentSize(65540);
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive-65536', $this);
|
||||||
|
$client->send($payload, 'text', false);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals($payload, $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertEquals(65540, $client->getFragmentSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiFragment(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive-multi-fragment', $this);
|
||||||
|
$client->setFragmentSize(8);
|
||||||
|
$client->send('Multi fragment test');
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals('Multi fragment test', $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertEquals(8, $client->getFragmentSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPingPong(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('ping-pong', $this);
|
||||||
|
$client->send('Server ping', 'ping');
|
||||||
|
$client->send('', 'ping');
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals('Receiving a message', $message);
|
||||||
|
$this->assertEquals('text', $client->getLastOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoteClose(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('close-remote', $this);
|
||||||
|
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertNull($message);
|
||||||
|
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$this->assertEquals(17260, $client->getCloseStatus());
|
||||||
|
$this->assertNull($client->getLastOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetTimeout(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('config-timeout', $this);
|
||||||
|
$client->setTimeout(300);
|
||||||
|
$this->assertTrue($client->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReconnect(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('client.close', $this);
|
||||||
|
$this->assertTrue($client->isConnected());
|
||||||
|
$this->assertNull($client->getCloseStatus());
|
||||||
|
$client->close();
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$this->assertEquals(1000, $client->getCloseStatus());
|
||||||
|
$this->assertNull($client->getLastOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('client.reconnect', $this);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertTrue($client->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPersistentConnection(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-persistent', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path', ['persistent' => true]);
|
||||||
|
$client->send('Connect');
|
||||||
|
$client->disconnect();
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedPersistentConnection(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-persistent-failure', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path', ['persistent' => true]);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionMessage('Could not resolve stream pointer position');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadScheme(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$this->expectException('WebSocket\BadUriException');
|
||||||
|
$this->expectExceptionMessage("Invalid URI scheme, must be 'ws' or 'wss'.");
|
||||||
|
$client = new Client('bad://localhost:8000/my/mock/path');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadUri(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$this->expectException('WebSocket\BadUriException');
|
||||||
|
$this->expectExceptionMessage("Invalid URI '--:this is not an uri:--' provided.");
|
||||||
|
$client = new Client('--:this is not an uri:--');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidUriType(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$this->expectException('WebSocket\BadUriException');
|
||||||
|
$this->expectExceptionMessage("Provided URI must be a UriInterface or string.");
|
||||||
|
$client = new Client([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUriInterface(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$uri = new Uri('ws://localhost:8000/my/mock/path');
|
||||||
|
$client = new Client($uri);
|
||||||
|
$client->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadStreamContext(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-bad-context', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path', ['context' => 'BAD']);
|
||||||
|
$this->expectException('InvalidArgumentException');
|
||||||
|
$this->expectExceptionMessage('Stream context in $options[\'context\'] isn\'t a valid context');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedConnection(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-failed', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Could not open socket to "localhost:8000"');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedConnectionWithError(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-error', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Could not open socket to "localhost:8000"');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadStreamConnection(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-bad-stream', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Invalid stream on "localhost:8000"');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandshakeFailure(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-handshake-failure', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Client handshake error');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidUpgrade(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-invalid-upgrade', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Connection to \'ws://localhost:8000/my/mock/path\' failed');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidKey(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-invalid-key', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Server sent bad upgrade response');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendBadOpcode(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
|
||||||
|
MockSocket::initialize('send-bad-opcode', $this);
|
||||||
|
$this->expectException('WebSocket\BadOpcodeException');
|
||||||
|
$this->expectExceptionMessage('Bad opcode \'bad\'. Try \'text\' or \'binary\'.');
|
||||||
|
$client->send('Bad Opcode', 'bad');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecieveBadOpcode(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('receive-bad-opcode', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1026);
|
||||||
|
$this->expectExceptionMessage('Bad opcode in websocket frame: 12');
|
||||||
|
$message = $client->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBrokenWrite(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('send-broken-write', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1025);
|
||||||
|
$this->expectExceptionMessage('Could only write 18 out of 22 bytes.');
|
||||||
|
$client->send('Failing to write');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedWrite(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('send-failed-write', $this);
|
||||||
|
$this->expectException('WebSocket\TimeoutException');
|
||||||
|
$this->expectExceptionCode(1024);
|
||||||
|
$this->expectExceptionMessage('Failed to write 22 bytes.');
|
||||||
|
$client->send('Failing to write');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBrokenRead(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('receive-broken-read', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1025);
|
||||||
|
$this->expectExceptionMessage('Broken frame, read 0 of stated 2 bytes.');
|
||||||
|
$client->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandshakeError(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect-handshake-error', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1024);
|
||||||
|
$this->expectExceptionMessage('Client handshake error');
|
||||||
|
$client->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReadTimeout(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('receive-client-timeout', $this);
|
||||||
|
$this->expectException('WebSocket\TimeoutException');
|
||||||
|
$this->expectExceptionCode(1024);
|
||||||
|
$this->expectExceptionMessage('Client read timeout');
|
||||||
|
$client->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmptyRead(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('receive-empty-read', $this);
|
||||||
|
$this->expectException('WebSocket\TimeoutException');
|
||||||
|
$this->expectExceptionCode(1024);
|
||||||
|
$this->expectExceptionMessage('Empty read; connection dead?');
|
||||||
|
$client->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFrameFragmentation(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client(
|
||||||
|
'ws://localhost:8000/my/mock/path',
|
||||||
|
['filter' => ['text', 'binary', 'pong', 'close']]
|
||||||
|
);
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('receive-fragmentation', $this);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals('Server ping', $message);
|
||||||
|
$this->assertEquals('pong', $client->getLastOpcode());
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals('Multi fragment test', $message);
|
||||||
|
$this->assertEquals('text', $client->getLastOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
MockSocket::initialize('close-remote', $this);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertEquals('Closing', $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$this->assertEquals(17260, $client->getCloseStatus());
|
||||||
|
$this->assertEquals('close', $client->getLastOpcode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageFragmentation(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client(
|
||||||
|
'ws://localhost:8000/my/mock/path',
|
||||||
|
['filter' => ['text', 'binary', 'pong', 'close'], 'return_obj' => true]
|
||||||
|
);
|
||||||
|
$client->send('Connect');
|
||||||
|
MockSocket::initialize('receive-fragmentation', $this);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Pong', $message);
|
||||||
|
$this->assertEquals('Server ping', $message->getContent());
|
||||||
|
$this->assertEquals('pong', $message->getOpcode());
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Text', $message);
|
||||||
|
$this->assertEquals('Multi fragment test', $message->getContent());
|
||||||
|
$this->assertEquals('text', $message->getOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
MockSocket::initialize('close-remote', $this);
|
||||||
|
$message = $client->receive();
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Close', $message);
|
||||||
|
$this->assertEquals('Closing', $message->getContent());
|
||||||
|
$this->assertEquals('close', $message->getOpcode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConvenicanceMethods(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('client.connect', $this);
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->assertNull($client->getName());
|
||||||
|
$this->assertNull($client->getRemoteName());
|
||||||
|
$this->assertEquals('WebSocket\Client(closed)', "{$client}");
|
||||||
|
$client->text('Connect');
|
||||||
|
MockSocket::initialize('send-convenicance', $this);
|
||||||
|
$client->binary(base64_encode('Binary content'));
|
||||||
|
$client->ping();
|
||||||
|
$client->pong();
|
||||||
|
$this->assertEquals('127.0.0.1:12345', $client->getName());
|
||||||
|
$this->assertEquals('127.0.0.1:8000', $client->getRemoteName());
|
||||||
|
$this->assertEquals('WebSocket\Client(127.0.0.1:12345)', "{$client}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnconnectedClient(): void
|
||||||
|
{
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$client->setTimeout(30);
|
||||||
|
$client->close();
|
||||||
|
$this->assertFalse($client->isConnected());
|
||||||
|
$this->assertNull($client->getName());
|
||||||
|
$this->assertNull($client->getRemoteName());
|
||||||
|
$this->assertNull($client->getCloseStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeprecated(): void
|
||||||
|
{
|
||||||
|
$client = new Client('ws://localhost:8000/my/mock/path');
|
||||||
|
(new ErrorHandler())->withAll(function () use ($client) {
|
||||||
|
$this->assertNull($client->getPier());
|
||||||
|
}, function ($exceptions, $result) {
|
||||||
|
$this->assertEquals(
|
||||||
|
'getPier() is deprecated and will be removed in future version. Use getRemoteName() instead.',
|
||||||
|
$exceptions[0]->getMessage()
|
||||||
|
);
|
||||||
|
}, E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
}
|
51
vendor/textalk/websocket/tests/ExceptionTest.php
vendored
Normal file
51
vendor/textalk/websocket/tests/ExceptionTest.php
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for Exceptions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ExceptionTest extends TestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
error_reporting(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnectionException(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
throw new ConnectionException(
|
||||||
|
'An error message',
|
||||||
|
ConnectionException::EOF,
|
||||||
|
['test' => 'with data'],
|
||||||
|
new TimeoutException(
|
||||||
|
'Nested exception',
|
||||||
|
ConnectionException::TIMED_OUT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertInstanceOf('WebSocket\ConnectionException', $e);
|
||||||
|
$this->assertInstanceOf('WebSocket\Exception', $e);
|
||||||
|
$this->assertInstanceOf('Exception', $e);
|
||||||
|
$this->assertInstanceOf('Throwable', $e);
|
||||||
|
$this->assertEquals('An error message', $e->getMessage());
|
||||||
|
$this->assertEquals(1025, $e->getCode());
|
||||||
|
$this->assertEquals(['test' => 'with data'], $e->getData());
|
||||||
|
|
||||||
|
$p = $e->getPrevious();
|
||||||
|
$this->assertInstanceOf('WebSocket\TimeoutException', $p);
|
||||||
|
$this->assertInstanceOf('WebSocket\ConnectionException', $p);
|
||||||
|
$this->assertEquals('Nested exception', $p->getMessage());
|
||||||
|
$this->assertEquals(1024, $p->getCode());
|
||||||
|
$this->assertEquals([], $p->getData());
|
||||||
|
}
|
||||||
|
}
|
60
vendor/textalk/websocket/tests/MessageTest.php
vendored
Normal file
60
vendor/textalk/websocket/tests/MessageTest.php
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for Message subsection.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use WebSocket\Message\Factory;
|
||||||
|
use WebSocket\Message\Text;
|
||||||
|
|
||||||
|
class MessageTest extends TestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
error_reporting(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFactory(): void
|
||||||
|
{
|
||||||
|
$factory = new Factory();
|
||||||
|
$message = $factory->create('text', 'Some content');
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Text', $message);
|
||||||
|
$message = $factory->create('binary', 'Some content');
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Binary', $message);
|
||||||
|
$message = $factory->create('ping', 'Some content');
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Ping', $message);
|
||||||
|
$message = $factory->create('pong', 'Some content');
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Pong', $message);
|
||||||
|
$message = $factory->create('close', 'Some content');
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Close', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessage()
|
||||||
|
{
|
||||||
|
$message = new Text('Some content');
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Text', $message);
|
||||||
|
$this->assertEquals('Some content', $message->getContent());
|
||||||
|
$this->assertEquals('text', $message->getOpcode());
|
||||||
|
$this->assertEquals(12, $message->getLength());
|
||||||
|
$this->assertTrue($message->hasContent());
|
||||||
|
$this->assertInstanceOf('DateTime', $message->getTimestamp());
|
||||||
|
$message->setContent('');
|
||||||
|
$this->assertEquals(0, $message->getLength());
|
||||||
|
$this->assertFalse($message->hasContent());
|
||||||
|
$this->assertEquals('WebSocket\Message\Text', "{$message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadOpcode()
|
||||||
|
{
|
||||||
|
$factory = new Factory();
|
||||||
|
$this->expectException('WebSocket\BadOpcodeException');
|
||||||
|
$this->expectExceptionMessage("Invalid opcode 'invalid' provided");
|
||||||
|
$message = $factory->create('invalid', 'Some content');
|
||||||
|
}
|
||||||
|
}
|
28
vendor/textalk/websocket/tests/README.md
vendored
Normal file
28
vendor/textalk/websocket/tests/README.md
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Testing
|
||||||
|
|
||||||
|
Unit tests with [PHPUnit](https://phpunit.readthedocs.io/).
|
||||||
|
|
||||||
|
|
||||||
|
## How to run
|
||||||
|
|
||||||
|
To run all test, run in console.
|
||||||
|
|
||||||
|
```
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Continuous integration
|
||||||
|
|
||||||
|
GitHub Actions are run on PHP versions `7.4`, `8.0`, `8.1` and `8.2`.
|
||||||
|
|
||||||
|
Code coverage by [Coveralls](https://coveralls.io/github/Textalk/websocket-php).
|
||||||
|
|
||||||
|
|
||||||
|
## Test strategy
|
||||||
|
|
||||||
|
Test set up overloads various stream and socket functions,
|
||||||
|
and use "scripts" to define and mock input/output of these functions.
|
||||||
|
|
||||||
|
This set up negates the dependency on running servers,
|
||||||
|
and allow testing various errors that might occur.
|
511
vendor/textalk/websocket/tests/ServerTest.php
vendored
Normal file
511
vendor/textalk/websocket/tests/ServerTest.php
vendored
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for Server.
|
||||||
|
* Note that this test is performed by mocking socket/stream calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WebSocket;
|
||||||
|
|
||||||
|
use ErrorException;
|
||||||
|
use Phrity\Util\ErrorHandler;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ServerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
error_reporting(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testServerMasked(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertEquals(8000, $server->getPort());
|
||||||
|
$this->assertEquals('/my/mock/path', $server->getPath());
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertEquals(4096, $server->getFragmentSize());
|
||||||
|
$this->assertNull($server->getCloseStatus());
|
||||||
|
$this->assertEquals([
|
||||||
|
'GET /my/mock/path HTTP/1.1',
|
||||||
|
'host: localhost:8000',
|
||||||
|
'user-agent: websocket-client-php',
|
||||||
|
'connection: Upgrade',
|
||||||
|
'upgrade: websocket',
|
||||||
|
'sec-websocket-key: cktLWXhUdDQ2OXF0ZCFqOQ==',
|
||||||
|
'sec-websocket-version: 13',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
], $server->getRequest());
|
||||||
|
$this->assertEquals('websocket-client-php', $server->getHeader('USER-AGENT'));
|
||||||
|
$this->assertNull($server->getHeader('no such header'));
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive', $this);
|
||||||
|
$server->send('Sending a message');
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('Receiving a message', $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertNull($server->getCloseStatus());
|
||||||
|
$this->assertEquals('text', $server->getLastOpcode());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.close', $this);
|
||||||
|
$server->close();
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$this->assertEquals(1000, $server->getCloseStatus());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
$server->close(); // Already closed
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDestruct(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept-destruct', $this);
|
||||||
|
$server->accept();
|
||||||
|
$message = $server->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testServerWithTimeout(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server(['timeout' => 300]);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept-timeout', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPayload128(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
$payload = file_get_contents(__DIR__ . '/mock/payload.128.txt');
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive-128', $this);
|
||||||
|
$server->send($payload, 'text', false);
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals($payload, $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPayload65536(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
$payload = file_get_contents(__DIR__ . '/mock/payload.65536.txt');
|
||||||
|
$server->setFragmentSize(65540);
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive-65536', $this);
|
||||||
|
$server->send($payload, 'text', false);
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals($payload, $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiFragment(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('send-receive-multi-fragment', $this);
|
||||||
|
$server->setFragmentSize(8);
|
||||||
|
$server->send('Multi fragment test');
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('Multi fragment test', $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPingPong(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('ping-pong', $this);
|
||||||
|
$server->send('Server ping', 'ping');
|
||||||
|
$server->send('', 'ping');
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('Receiving a message', $message);
|
||||||
|
$this->assertEquals('text', $server->getLastOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoteClose(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('close-remote', $this);
|
||||||
|
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('', $message);
|
||||||
|
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$this->assertEquals(17260, $server->getCloseStatus());
|
||||||
|
$this->assertNull($server->getLastOpcode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetTimeout(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('config-timeout', $this);
|
||||||
|
$server->setTimeout(300);
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedSocketServer(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct-failed-socket-server', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Could not open listening socket:');
|
||||||
|
$server = new Server(['port' => 9999]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedSocketServerWithError(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct-error-socket-server', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Could not open listening socket:');
|
||||||
|
$server = new Server(['port' => 9999]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedConnect(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept-failed-connect', $this);
|
||||||
|
$server->accept();
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Server failed to connect');
|
||||||
|
$server->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedConnectWithError(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept-error-connect', $this);
|
||||||
|
$server->accept();
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Server failed to connect');
|
||||||
|
$server->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedConnectTimeout(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server(['timeout' => 300]);
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept-failed-connect', $this);
|
||||||
|
$server->accept();
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Server failed to connect');
|
||||||
|
$server->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedHttp(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept-failed-http', $this);
|
||||||
|
$server->accept();
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('No GET in request');
|
||||||
|
$server->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedWsKey(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept-failed-ws-key', $this);
|
||||||
|
$server->accept();
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Client had no Key in upgrade request');
|
||||||
|
$server->send('Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendBadOpcode(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->expectException('WebSocket\BadOpcodeException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Bad opcode \'bad\'. Try \'text\' or \'binary\'.');
|
||||||
|
$server->send('Bad Opcode', 'bad');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecieveBadOpcode(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('receive-bad-opcode', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1026);
|
||||||
|
$this->expectExceptionMessage('Bad opcode in websocket frame: 12');
|
||||||
|
$message = $server->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBrokenWrite(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('send-broken-write', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1025);
|
||||||
|
$this->expectExceptionMessage('Could only write 18 out of 22 bytes.');
|
||||||
|
$server->send('Failing to write');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedWrite(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('send-failed-write', $this);
|
||||||
|
$this->expectException('WebSocket\TimeoutException');
|
||||||
|
$this->expectExceptionCode(1024);
|
||||||
|
$this->expectExceptionMessage('Failed to write 22 bytes.');
|
||||||
|
$server->send('Failing to write');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBrokenRead(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('receive-broken-read', $this);
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(1025);
|
||||||
|
$this->expectExceptionMessage('Broken frame, read 0 of stated 2 bytes.');
|
||||||
|
$server->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmptyRead(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('receive-empty-read', $this);
|
||||||
|
$this->expectException('WebSocket\TimeoutException');
|
||||||
|
$this->expectExceptionCode(1024);
|
||||||
|
$this->expectExceptionMessage('Empty read; connection dead?');
|
||||||
|
$server->receive();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFrameFragmentation(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server(['filter' => ['text', 'binary', 'pong', 'close']]);
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('receive-fragmentation', $this);
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('Server ping', $message);
|
||||||
|
$this->assertEquals('pong', $server->getLastOpcode());
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('Multi fragment test', $message);
|
||||||
|
$this->assertEquals('text', $server->getLastOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
MockSocket::initialize('close-remote', $this);
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertEquals('Closing', $message);
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$this->assertEquals(17260, $server->getCloseStatus());
|
||||||
|
$this->assertEquals('close', $server->getLastOpcode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageFragmentation(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server(['filter' => ['text', 'binary', 'pong', 'close'], 'return_obj' => true]);
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
MockSocket::initialize('receive-fragmentation', $this);
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Pong', $message);
|
||||||
|
$this->assertEquals('Server ping', $message->getContent());
|
||||||
|
$this->assertEquals('pong', $message->getOpcode());
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Text', $message);
|
||||||
|
$this->assertEquals('Multi fragment test', $message->getContent());
|
||||||
|
$this->assertEquals('text', $message->getOpcode());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
MockSocket::initialize('close-remote', $this);
|
||||||
|
$message = $server->receive();
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Message', $message);
|
||||||
|
$this->assertInstanceOf('WebSocket\Message\Close', $message);
|
||||||
|
$this->assertEquals('Closing', $message->getContent());
|
||||||
|
$this->assertEquals('close', $message->getOpcode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConvenicanceMethods(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertNull($server->getName());
|
||||||
|
$this->assertNull($server->getRemoteName());
|
||||||
|
$this->assertEquals('WebSocket\Server(closed)', "{$server}");
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->text('Connect');
|
||||||
|
MockSocket::initialize('send-convenicance', $this);
|
||||||
|
$server->binary(base64_encode('Binary content'));
|
||||||
|
$server->ping();
|
||||||
|
$server->pong();
|
||||||
|
$this->assertEquals('127.0.0.1:12345', $server->getName());
|
||||||
|
$this->assertEquals('127.0.0.1:8000', $server->getRemoteName());
|
||||||
|
$this->assertEquals('WebSocket\Server(127.0.0.1:12345)', "{$server}");
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnconnectedServer(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$server->setTimeout(30);
|
||||||
|
$server->close();
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$this->assertNull($server->getName());
|
||||||
|
$this->assertNull($server->getRemoteName());
|
||||||
|
$this->assertNull($server->getCloseStatus());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFailedHandshake(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.accept-failed-handshake', $this);
|
||||||
|
$server->accept();
|
||||||
|
$this->expectException('WebSocket\ConnectionException');
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage('Could not read from stream');
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testServerDisconnect(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
MockSocket::initialize('server.accept', $this);
|
||||||
|
$server->accept();
|
||||||
|
$server->send('Connect');
|
||||||
|
$this->assertTrue($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
|
||||||
|
MockSocket::initialize('server.disconnect', $this);
|
||||||
|
$server->disconnect();
|
||||||
|
$this->assertFalse($server->isConnected());
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDeprecated(): void
|
||||||
|
{
|
||||||
|
MockSocket::initialize('server.construct', $this);
|
||||||
|
$server = new Server();
|
||||||
|
$this->assertTrue(MockSocket::isEmpty());
|
||||||
|
(new ErrorHandler())->withAll(function () use ($server) {
|
||||||
|
$this->assertNull($server->getPier());
|
||||||
|
}, function ($exceptions, $result) {
|
||||||
|
$this->assertEquals(
|
||||||
|
'getPier() is deprecated and will be removed in future version. Use getRemoteName() instead.',
|
||||||
|
$exceptions[0]->getMessage()
|
||||||
|
);
|
||||||
|
}, E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user