mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 01:39:24 +02:00
Adding socket support
-- When upload a video to a certain category, this video will be part of a user group automatically -- one category can add your video in more than one user group -- Allow user add some extra info in JSON format, for example bank info and other thing -- This is based on users_extra_info table -- add full text search https://github.com/WWBN/AVideo/issues/4343
This commit is contained in:
parent
939ad22ab1
commit
730f4893f2
659 changed files with 80151 additions and 1694 deletions
|
@ -24,6 +24,8 @@
|
||||||
"monolog/monolog": "^1.17",
|
"monolog/monolog": "^1.17",
|
||||||
"phpmailer/phpmailer": "^6.2",
|
"phpmailer/phpmailer": "^6.2",
|
||||||
"psr/cache": "^1.0",
|
"psr/cache": "^1.0",
|
||||||
"psr/http-message": "^1.0"
|
"psr/http-message": "^1.0",
|
||||||
|
"cboden/ratchet": "^0.4.3",
|
||||||
|
"ratchet/pawl": "^0.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
845
composer.lock
generated
845
composer.lock
generated
|
@ -4,8 +4,65 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "a1645cd1c21c2df726fd9682677ec1ca",
|
"content-hash": "2f88f50da37e688690e40fdb3f9b4d00",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "cboden/ratchet",
|
||||||
|
"version": "v0.4.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ratchetphp/Ratchet.git",
|
||||||
|
"reference": "466a0ecc83209c75b76645eb823401b5c52e5f21"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ratchetphp/Ratchet/zipball/466a0ecc83209c75b76645eb823401b5c52e5f21",
|
||||||
|
"reference": "466a0ecc83209c75b76645eb823401b5c52e5f21",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"guzzlehttp/psr7": "^1.0",
|
||||||
|
"php": ">=5.4.2",
|
||||||
|
"ratchet/rfc6455": "^0.3",
|
||||||
|
"react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5",
|
||||||
|
"symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0",
|
||||||
|
"symfony/routing": "^2.6|^3.0|^4.0|^5.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "~4.8"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Ratchet\\": "src/Ratchet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Chris Boden",
|
||||||
|
"email": "cboden@gmail.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Matt Bonneau",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP WebSocket library",
|
||||||
|
"homepage": "http://socketo.me",
|
||||||
|
"keywords": [
|
||||||
|
"Ratchet",
|
||||||
|
"WebSockets",
|
||||||
|
"server",
|
||||||
|
"sockets",
|
||||||
|
"websocket"
|
||||||
|
],
|
||||||
|
"time": "2020-07-07T15:50:14+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "chrisjean/php-ico",
|
"name": "chrisjean/php-ico",
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
|
@ -53,6 +110,54 @@
|
||||||
},
|
},
|
||||||
"time": "2016-09-27T22:00:56+00:00"
|
"time": "2016-09-27T22:00:56+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "evenement/evenement",
|
||||||
|
"version": "v2.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/igorw/evenement.git",
|
||||||
|
"reference": "6ba9a777870ab49f417e703229d53931ed40fd7a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/igorw/evenement/zipball/6ba9a777870ab49f417e703229d53931ed40fd7a",
|
||||||
|
"reference": "6ba9a777870ab49f417e703229d53931ed40fd7a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^6.0||^5.7||^4.8.35"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Evenement": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Igor Wiedler",
|
||||||
|
"email": "igor@wiedler.ch"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Événement is a very simple event dispatching library for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"event-dispatcher",
|
||||||
|
"event-emitter"
|
||||||
|
],
|
||||||
|
"time": "2017-07-17T17:39:19+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "ezyang/htmlpurifier",
|
"name": "ezyang/htmlpurifier",
|
||||||
"version": "v4.13.0",
|
"version": "v4.13.0",
|
||||||
|
@ -730,16 +835,6 @@
|
||||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||||
"source": "https://github.com/Seldaek/monolog/tree/1.26.0"
|
"source": "https://github.com/Seldaek/monolog/tree/1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/Seldaek",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-12-14T12:56:38+00:00"
|
"time": "2020-12-14T12:56:38+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -864,12 +959,6 @@
|
||||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.2.0"
|
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.2.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/Synchro",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-11-25T15:24:57+00:00"
|
"time": "2020-11-25T15:24:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -965,20 +1054,6 @@
|
||||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||||
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.30"
|
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.30"
|
||||||
},
|
},
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/terrafrost",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/phpseclib",
|
|
||||||
"type": "patreon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-12-17T05:42:04+00:00"
|
"time": "2020-12-17T05:42:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1177,6 +1252,541 @@
|
||||||
},
|
},
|
||||||
"time": "2019-03-08T08:55:37+00:00"
|
"time": "2019-03-08T08:55:37+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ratchet/pawl",
|
||||||
|
"version": "v0.3.5",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ratchetphp/Pawl.git",
|
||||||
|
"reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ratchetphp/Pawl/zipball/89ec703c76dc893484a2a0ed44b48a37d445abd5",
|
||||||
|
"reference": "89ec703c76dc893484a2a0ed44b48a37d445abd5",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"evenement/evenement": "^3.0 || ^2.0",
|
||||||
|
"php": ">=5.4",
|
||||||
|
"ratchet/rfc6455": "^0.3",
|
||||||
|
"react/socket": "^1.0 || ^0.8 || ^0.7"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "~4.8"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"reactivex/rxphp": "~2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Ratchet\\Client\\": "src"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Asynchronous WebSocket client",
|
||||||
|
"keywords": [
|
||||||
|
"Ratchet",
|
||||||
|
"async",
|
||||||
|
"client",
|
||||||
|
"websocket",
|
||||||
|
"websocket client"
|
||||||
|
],
|
||||||
|
"time": "2020-07-17T15:32:47+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ratchet/rfc6455",
|
||||||
|
"version": "v0.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ratchetphp/RFC6455.git",
|
||||||
|
"reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/c8651c7938651c2d55f5d8c2422ac5e57a183341",
|
||||||
|
"reference": "c8651c7938651c2d55f5d8c2422ac5e57a183341",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"guzzlehttp/psr7": "^1.0",
|
||||||
|
"php": ">=5.4.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "5.7.*",
|
||||||
|
"react/socket": "^1.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Ratchet\\RFC6455\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Chris Boden",
|
||||||
|
"email": "cboden@gmail.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Matt Bonneau",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "RFC6455 WebSocket protocol handler",
|
||||||
|
"homepage": "http://socketo.me",
|
||||||
|
"keywords": [
|
||||||
|
"WebSockets",
|
||||||
|
"rfc6455",
|
||||||
|
"websocket"
|
||||||
|
],
|
||||||
|
"time": "2020-05-15T18:31:24+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/cache",
|
||||||
|
"version": "v1.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/cache.git",
|
||||||
|
"reference": "44a568925556b0bd8cacc7b49fb0f1cf0d706a0c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/cache/zipball/44a568925556b0bd8cacc7b49fb0f1cf0d706a0c",
|
||||||
|
"reference": "44a568925556b0bd8cacc7b49fb0f1cf0d706a0c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0",
|
||||||
|
"react/promise": "^3.0 || ^2.0 || ^1.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Cache\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christian Lück",
|
||||||
|
"email": "christian@clue.engineering",
|
||||||
|
"homepage": "https://clue.engineering/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cees-Jan Kiewiet",
|
||||||
|
"email": "reactphp@ceesjankiewiet.nl",
|
||||||
|
"homepage": "https://wyrihaximus.net/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jan Sorgalla",
|
||||||
|
"email": "jsorgalla@gmail.com",
|
||||||
|
"homepage": "https://sorgalla.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chris Boden",
|
||||||
|
"email": "cboden@gmail.com",
|
||||||
|
"homepage": "https://cboden.dev/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Async, Promise-based cache interface for ReactPHP",
|
||||||
|
"keywords": [
|
||||||
|
"cache",
|
||||||
|
"caching",
|
||||||
|
"promise",
|
||||||
|
"reactphp"
|
||||||
|
],
|
||||||
|
"time": "2020-09-18T12:12:35+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/dns",
|
||||||
|
"version": "v1.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/dns.git",
|
||||||
|
"reference": "665260757171e2ab17485b44e7ffffa7acb6ca1f"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/dns/zipball/665260757171e2ab17485b44e7ffffa7acb6ca1f",
|
||||||
|
"reference": "665260757171e2ab17485b44e7ffffa7acb6ca1f",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0",
|
||||||
|
"react/cache": "^1.0 || ^0.6 || ^0.5",
|
||||||
|
"react/event-loop": "^1.0 || ^0.5",
|
||||||
|
"react/promise": "^3.0 || ^2.7 || ^1.2.1",
|
||||||
|
"react/promise-timer": "^1.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"clue/block-react": "^1.2",
|
||||||
|
"phpunit/phpunit": "^9.3 || ^4.8.35"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Dns\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christian Lück",
|
||||||
|
"email": "christian@clue.engineering",
|
||||||
|
"homepage": "https://clue.engineering/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cees-Jan Kiewiet",
|
||||||
|
"email": "reactphp@ceesjankiewiet.nl",
|
||||||
|
"homepage": "https://wyrihaximus.net/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jan Sorgalla",
|
||||||
|
"email": "jsorgalla@gmail.com",
|
||||||
|
"homepage": "https://sorgalla.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chris Boden",
|
||||||
|
"email": "cboden@gmail.com",
|
||||||
|
"homepage": "https://cboden.dev/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Async DNS resolver for ReactPHP",
|
||||||
|
"keywords": [
|
||||||
|
"async",
|
||||||
|
"dns",
|
||||||
|
"dns-resolver",
|
||||||
|
"reactphp"
|
||||||
|
],
|
||||||
|
"time": "2020-09-18T12:12:55+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/event-loop",
|
||||||
|
"version": "v1.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/event-loop.git",
|
||||||
|
"reference": "6d24de090cd59cfc830263cfba965be77b563c13"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13",
|
||||||
|
"reference": "6d24de090cd59cfc830263cfba965be77b563c13",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-event": "~1.0 for ExtEventLoop",
|
||||||
|
"ext-pcntl": "For signal handling support when using the StreamSelectLoop",
|
||||||
|
"ext-uv": "* for ExtUvLoop"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\EventLoop\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.",
|
||||||
|
"keywords": [
|
||||||
|
"asynchronous",
|
||||||
|
"event-loop"
|
||||||
|
],
|
||||||
|
"time": "2020-01-01T18:39:52+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/promise",
|
||||||
|
"version": "v2.8.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/promise.git",
|
||||||
|
"reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/promise/zipball/f3cff96a19736714524ca0dd1d4130de73dbbbc4",
|
||||||
|
"reference": "f3cff96a19736714524ca0dd1d4130de73dbbbc4",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^7.0 || ^6.5 || ^5.7 || ^4.8.36"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Promise\\": "src/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jan Sorgalla",
|
||||||
|
"email": "jsorgalla@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"promise",
|
||||||
|
"promises"
|
||||||
|
],
|
||||||
|
"time": "2020-05-12T15:16:56+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/promise-timer",
|
||||||
|
"version": "v1.6.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/promise-timer.git",
|
||||||
|
"reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/promise-timer/zipball/daee9baf6ef30c43ea4c86399f828bb5f558f6e6",
|
||||||
|
"reference": "daee9baf6ef30c43ea4c86399f828bb5f558f6e6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3",
|
||||||
|
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
|
||||||
|
"react/promise": "^3.0 || ^2.7.0 || ^1.2.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.0 || ^5.7 || ^4.8.35"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Promise\\Timer\\": "src/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christian Lück",
|
||||||
|
"email": "christian@lueck.tv"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.",
|
||||||
|
"homepage": "https://github.com/reactphp/promise-timer",
|
||||||
|
"keywords": [
|
||||||
|
"async",
|
||||||
|
"event-loop",
|
||||||
|
"promise",
|
||||||
|
"reactphp",
|
||||||
|
"timeout",
|
||||||
|
"timer"
|
||||||
|
],
|
||||||
|
"time": "2020-07-10T12:18:06+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/socket",
|
||||||
|
"version": "v1.6.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/socket.git",
|
||||||
|
"reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/socket/zipball/e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
|
||||||
|
"reference": "e2b96b23a13ca9b41ab343268dbce3f8ef4d524a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
|
||||||
|
"php": ">=5.3.0",
|
||||||
|
"react/dns": "^1.1",
|
||||||
|
"react/event-loop": "^1.0 || ^0.5",
|
||||||
|
"react/promise": "^2.6.0 || ^1.2.1",
|
||||||
|
"react/promise-timer": "^1.4.0",
|
||||||
|
"react/stream": "^1.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"clue/block-react": "^1.2",
|
||||||
|
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35",
|
||||||
|
"react/promise-stream": "^1.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Socket\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christian Lück",
|
||||||
|
"email": "christian@clue.engineering",
|
||||||
|
"homepage": "https://clue.engineering/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cees-Jan Kiewiet",
|
||||||
|
"email": "reactphp@ceesjankiewiet.nl",
|
||||||
|
"homepage": "https://wyrihaximus.net/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jan Sorgalla",
|
||||||
|
"email": "jsorgalla@gmail.com",
|
||||||
|
"homepage": "https://sorgalla.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chris Boden",
|
||||||
|
"email": "cboden@gmail.com",
|
||||||
|
"homepage": "https://cboden.dev/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP",
|
||||||
|
"keywords": [
|
||||||
|
"Connection",
|
||||||
|
"Socket",
|
||||||
|
"async",
|
||||||
|
"reactphp",
|
||||||
|
"stream"
|
||||||
|
],
|
||||||
|
"time": "2020-08-28T12:49:05+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react/stream",
|
||||||
|
"version": "v1.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reactphp/stream.git",
|
||||||
|
"reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reactphp/stream/zipball/7c02b510ee3f582c810aeccd3a197b9c2f52ff1a",
|
||||||
|
"reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
|
||||||
|
"php": ">=5.3.8",
|
||||||
|
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"clue/stream-filter": "~1.2",
|
||||||
|
"phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"React\\Stream\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP",
|
||||||
|
"keywords": [
|
||||||
|
"event-driven",
|
||||||
|
"io",
|
||||||
|
"non-blocking",
|
||||||
|
"pipe",
|
||||||
|
"reactphp",
|
||||||
|
"readable",
|
||||||
|
"stream",
|
||||||
|
"writable"
|
||||||
|
],
|
||||||
|
"time": "2020-05-04T10:17:57+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/http-foundation",
|
||||||
|
"version": "v3.4.47",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/http-foundation.git",
|
||||||
|
"reference": "b9885fcce6fe494201da4f70a9309770e9d13dc8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/b9885fcce6fe494201da4f70a9309770e9d13dc8",
|
||||||
|
"reference": "b9885fcce6fe494201da4f70a9309770e9d13dc8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^5.5.9|>=7.0.8",
|
||||||
|
"symfony/polyfill-mbstring": "~1.1",
|
||||||
|
"symfony/polyfill-php70": "~1.6"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/expression-language": "~2.8|~3.0|~4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\HttpFoundation\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony HttpFoundation Component",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"time": "2020-10-24T10:57:07+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-idn",
|
"name": "symfony/polyfill-intl-idn",
|
||||||
"version": "v1.19.0",
|
"version": "v1.19.0",
|
||||||
|
@ -1249,20 +1859,6 @@
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.19.0"
|
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.19.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://symfony.com/sponsor",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/fabpot",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-10-21T09:57:48+00:00"
|
"time": "2020-10-21T09:57:48+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1333,19 +1929,68 @@
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.19.0"
|
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.19.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"time": "2020-10-23T09:01:57+00:00"
|
||||||
{
|
|
||||||
"url": "https://symfony.com/sponsor",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fabpot",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"type": "github"
|
"version": "v1.19.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce"
|
||||||
},
|
},
|
||||||
{
|
"dist": {
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"type": "zip",
|
||||||
"type": "tidelift"
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b5f7b932ee6fa802fc792eabd77c4c88084517ce",
|
||||||
|
"reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.19-dev"
|
||||||
|
},
|
||||||
|
"thanks": {
|
||||||
|
"name": "symfony/polyfill",
|
||||||
|
"url": "https://github.com/symfony/polyfill"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for the Mbstring extension",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"mbstring",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
],
|
],
|
||||||
"time": "2020-10-23T09:01:57+00:00"
|
"time": "2020-10-23T09:01:57+00:00"
|
||||||
},
|
},
|
||||||
|
@ -1413,20 +2058,6 @@
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php70/tree/v1.19.0"
|
"source": "https://github.com/symfony/polyfill-php70/tree/v1.19.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://symfony.com/sponsor",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/fabpot",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-10-23T09:01:57+00:00"
|
"time": "2020-10-23T09:01:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1489,21 +2120,78 @@
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php72/tree/v1.19.0"
|
"source": "https://github.com/symfony/polyfill-php72/tree/v1.19.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"time": "2020-10-23T09:01:57+00:00"
|
||||||
{
|
|
||||||
"url": "https://symfony.com/sponsor",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "https://github.com/fabpot",
|
"name": "symfony/routing",
|
||||||
"type": "github"
|
"version": "v3.4.47",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/routing.git",
|
||||||
|
"reference": "3e522ac69cadffd8131cc2b22157fa7662331a6c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/routing/zipball/3e522ac69cadffd8131cc2b22157fa7662331a6c",
|
||||||
|
"reference": "3e522ac69cadffd8131cc2b22157fa7662331a6c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^5.5.9|>=7.0.8"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"symfony/config": "<3.3.1",
|
||||||
|
"symfony/dependency-injection": "<3.3",
|
||||||
|
"symfony/yaml": "<3.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/annotations": "~1.0",
|
||||||
|
"psr/log": "~1.0",
|
||||||
|
"symfony/config": "^3.3.1|~4.0",
|
||||||
|
"symfony/dependency-injection": "~3.3|~4.0",
|
||||||
|
"symfony/expression-language": "~2.8|~3.0|~4.0",
|
||||||
|
"symfony/http-foundation": "~2.8|~3.0|~4.0",
|
||||||
|
"symfony/yaml": "~3.4|~4.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"doctrine/annotations": "For using the annotation loader",
|
||||||
|
"symfony/config": "For using the all-in-one router or any loader",
|
||||||
|
"symfony/expression-language": "For using expression matching",
|
||||||
|
"symfony/http-foundation": "For using a Symfony Request object",
|
||||||
|
"symfony/yaml": "For using the YAML loader"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\Routing\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
"name": "Symfony Community",
|
||||||
"type": "tidelift"
|
"homepage": "https://symfony.com/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-10-23T09:01:57+00:00"
|
"description": "Symfony Routing Component",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"router",
|
||||||
|
"routing",
|
||||||
|
"uri",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"time": "2020-10-24T10:57:07+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [],
|
"packages-dev": [],
|
||||||
|
@ -1520,6 +2208,5 @@
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "5.6.0"
|
"php": "5.6.0"
|
||||||
},
|
}
|
||||||
"plugin-api-version": "2.0.0"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ if (file_exists("../videos/configuration.php")) {
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$installationVersion = "10.0";
|
$installationVersion = "10.1";
|
||||||
|
|
||||||
error_log("Installation: ".__LINE__." ". json_encode($_POST));
|
error_log("Installation: ".__LINE__." ". json_encode($_POST));
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
|
@ -38,6 +38,7 @@ CREATE TABLE IF NOT EXISTS `users` (
|
||||||
`region` VARCHAR(100) NULL DEFAULT NULL,
|
`region` VARCHAR(100) NULL DEFAULT NULL,
|
||||||
`city` VARCHAR(100) NULL DEFAULT NULL,
|
`city` VARCHAR(100) NULL DEFAULT NULL,
|
||||||
`donationLink` VARCHAR(225) NULL DEFAULT NULL,
|
`donationLink` VARCHAR(225) NULL DEFAULT NULL,
|
||||||
|
`extra_info` TEXT NULL DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE INDEX `user_UNIQUE` (`user` ASC))
|
UNIQUE INDEX `user_UNIQUE` (`user` ASC))
|
||||||
ENGINE = InnoDB;
|
ENGINE = InnoDB;
|
||||||
|
@ -81,6 +82,8 @@ CREATE TABLE IF NOT EXISTS `categories` (
|
||||||
INDEX `clean_name_INDEX2` (`clean_name` ASC),
|
INDEX `clean_name_INDEX2` (`clean_name` ASC),
|
||||||
INDEX `sortcategoryOrderIndex` (`order` ASC),
|
INDEX `sortcategoryOrderIndex` (`order` ASC),
|
||||||
INDEX `category_name_idx` (`name` ASC),
|
INDEX `category_name_idx` (`name` ASC),
|
||||||
|
FULLTEXT INDEX `index7cname` (`name`),
|
||||||
|
FULLTEXT INDEX `index8cdescr` (`description`),
|
||||||
UNIQUE INDEX `clean_name_UNIQUE` (`clean_name` ASC),
|
UNIQUE INDEX `clean_name_UNIQUE` (`clean_name` ASC),
|
||||||
CONSTRAINT `fk_categories_users1`
|
CONSTRAINT `fk_categories_users1`
|
||||||
FOREIGN KEY (`users_id`)
|
FOREIGN KEY (`users_id`)
|
||||||
|
@ -155,6 +158,8 @@ CREATE TABLE IF NOT EXISTS `videos` (
|
||||||
INDEX `video_filename_INDEX` (`filename` ASC),
|
INDEX `video_filename_INDEX` (`filename` ASC),
|
||||||
INDEX `video_status_idx` (`status` ASC),
|
INDEX `video_status_idx` (`status` ASC),
|
||||||
INDEX `video_type_idx` (`type` ASC) ,
|
INDEX `video_type_idx` (`type` ASC) ,
|
||||||
|
FULLTEXT INDEX `index17vname` (`title`),
|
||||||
|
FULLTEXT INDEX `index18vdesc` (`description`),
|
||||||
CONSTRAINT `fk_videos_sites1`
|
CONSTRAINT `fk_videos_sites1`
|
||||||
FOREIGN KEY (`sites_id`)
|
FOREIGN KEY (`sites_id`)
|
||||||
REFERENCES `sites` (`id`)
|
REFERENCES `sites` (`id`)
|
||||||
|
@ -508,6 +513,41 @@ CREATE TABLE `category_type_cache` (
|
||||||
|
|
||||||
) ENGINE=InnoDB;
|
) ENGINE=InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `categories_has_users_groups` (
|
||||||
|
`id` INT NOT NULL AUTO_INCREMENT,
|
||||||
|
`categories_id` INT(11) NOT NULL,
|
||||||
|
`users_groups_id` INT(11) NOT NULL,
|
||||||
|
`created` DATETIME NULL,
|
||||||
|
`modified` DATETIME NULL,
|
||||||
|
`status` CHAR(1) NOT NULL DEFAULT 'a',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `fk_categories_has_users_groups_users_groups1_idx` (`users_groups_id` ASC),
|
||||||
|
INDEX `fk_categories_has_users_groups_categories1_idx` (`categories_id` ASC),
|
||||||
|
CONSTRAINT `fk_categories_has_users_groups_categories1`
|
||||||
|
FOREIGN KEY (`categories_id`)
|
||||||
|
REFERENCES `categories` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_categories_has_users_groups_users_groups1`
|
||||||
|
FOREIGN KEY (`users_groups_id`)
|
||||||
|
REFERENCES `users_groups` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE)
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `users_extra_info` (
|
||||||
|
`id` INT NOT NULL AUTO_INCREMENT,
|
||||||
|
`field_name` VARCHAR(45) NOT NULL,
|
||||||
|
`field_type` VARCHAR(45) NOT NULL,
|
||||||
|
`field_options` TEXT NULL,
|
||||||
|
`field_default_value` VARCHAR(45) NULL,
|
||||||
|
`parameters` TEXT NULL,
|
||||||
|
`created` DATETIME NULL,
|
||||||
|
`modified` DATETIME NULL,
|
||||||
|
`status` CHAR(1) NOT NULL DEFAULT 'a',
|
||||||
|
PRIMARY KEY (`id`))
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
ALTER TABLE `category_type_cache`
|
ALTER TABLE `category_type_cache`
|
||||||
ADD UNIQUE KEY `categoryId` (`categoryId`);
|
ADD UNIQUE KEY `categoryId` (`categoryId`);
|
||||||
|
|
||||||
|
|
|
@ -88,8 +88,7 @@ abstract class ObjectYPT implements ObjectInterface
|
||||||
return $row;
|
return $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAll()
|
public static function getAll(){
|
||||||
{
|
|
||||||
global $global;
|
global $global;
|
||||||
if (!static::isTableInstalled()) {
|
if (!static::isTableInstalled()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -111,6 +110,28 @@ abstract class ObjectYPT implements ObjectInterface
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getAllActive(){
|
||||||
|
global $global;
|
||||||
|
if (!static::isTableInstalled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sql = "SELECT * FROM " . static::getTableName() . " WHERE status='a' ";
|
||||||
|
|
||||||
|
$sql .= self::getSqlFromPost();
|
||||||
|
$res = sqlDAL::readSql($sql);
|
||||||
|
$fullData = sqlDAL::fetchAllAssoc($res);
|
||||||
|
sqlDAL::close($res);
|
||||||
|
$rows = array();
|
||||||
|
if ($res != false) {
|
||||||
|
foreach ($fullData as $row) {
|
||||||
|
$rows[] = $row;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error);
|
||||||
|
}
|
||||||
|
return $rows;
|
||||||
|
}
|
||||||
|
|
||||||
public static function getTotal()
|
public static function getTotal()
|
||||||
{
|
{
|
||||||
//will receive
|
//will receive
|
||||||
|
|
5
objects/cboden/ratchet/.gitignore
vendored
Normal file
5
objects/cboden/ratchet/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
phpunit.xml
|
||||||
|
reports
|
||||||
|
sandbox
|
||||||
|
vendor
|
||||||
|
composer.lock
|
15
objects/cboden/ratchet/.travis.yml
Normal file
15
objects/cboden/ratchet/.travis.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
language: php
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.4
|
||||||
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
- 7.0
|
||||||
|
- 7.1
|
||||||
|
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "session.serialize_handler = php" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
|
||||||
|
- php -m
|
||||||
|
- composer install --dev --prefer-source
|
145
objects/cboden/ratchet/CHANGELOG.md
Normal file
145
objects/cboden/ratchet/CHANGELOG.md
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
### Legend
|
||||||
|
|
||||||
|
* "BC": Backwards compatibility break (from public component APIs)
|
||||||
|
* "BF": Bug fix
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* 0.4.3 (2020-06-04)
|
||||||
|
* BF: Fixed interface acceptable regression in `App`
|
||||||
|
* Update RFC6455 library with latest fixes
|
||||||
|
|
||||||
|
* 0.4.2 (2020-01-27)
|
||||||
|
* Support Symfony 5
|
||||||
|
* BF: Use phpunit from vendor directory
|
||||||
|
* Allow disabling of xdebug warning by defining `RATCHET_DISABLE_XDEBUG_WARN`
|
||||||
|
* Stop using `LoopInterface::tick()` for testing
|
||||||
|
|
||||||
|
* 0.4.1 (2017-12-11)
|
||||||
|
* Only enableKeepAlive in App if no WsServer passed allowing user to set their own timeout duration
|
||||||
|
* Support Symfony 4
|
||||||
|
* BF: Plug NOOP controller in connection from router in case of misbehaving client
|
||||||
|
* BF: Raise error from invalid WAMP payload
|
||||||
|
|
||||||
|
* 0.4 (2017-09-14)
|
||||||
|
* BC: $conn->WebSocket->request replaced with $conn->httpRequest which is a PSR-7 object
|
||||||
|
* Binary messages now supported via Ratchet\WebSocket\MessageComponentInterface
|
||||||
|
* Added heartbeat support via ping/pong in WsServer
|
||||||
|
* BC: No longer support old (and insecure) Hixie76 and Hybi protocols
|
||||||
|
* BC: No longer support disabling UTF-8 checks
|
||||||
|
* BC: The Session component implements HttpServerInterface instead of WsServerInterface
|
||||||
|
* BC: PHP 5.3 no longer supported
|
||||||
|
* BC: Update to newer version of react/socket dependency
|
||||||
|
* BC: WAMP topics reduced to 0 subscriptions are deleted, new subs to same name will result in new Topic instance
|
||||||
|
* Significant performance enhancements
|
||||||
|
|
||||||
|
* 0.3.6 (2017-01-06)
|
||||||
|
* BF: Keep host and scheme in HTTP request object attatched to connection
|
||||||
|
* BF: Return correct HTTP response (405) when non-GET request made
|
||||||
|
|
||||||
|
* 0.3.5 (2016-05-25)
|
||||||
|
* BF: Unmask responding close frame
|
||||||
|
* Added write handler for PHP session serializer
|
||||||
|
|
||||||
|
* 0.3.4 (2015-12-23)
|
||||||
|
* BF: Edge case where version check wasn't run on message coalesce
|
||||||
|
* BF: Session didn't start when using pdo_sqlite
|
||||||
|
* BF: WAMP currie prefix check when using '#'
|
||||||
|
* Compatibility with Symfony 3
|
||||||
|
|
||||||
|
* 0.3.3 (2015-05-26)
|
||||||
|
* BF: Framing bug on large messages upon TCP fragmentation
|
||||||
|
* BF: Symfony Router query parameter defaults applied to Request
|
||||||
|
* BF: WAMP CURIE on all URIs
|
||||||
|
* OriginCheck rules applied to FlashPolicy
|
||||||
|
* Switched from PSR-0 to PSR-4
|
||||||
|
|
||||||
|
* 0.3.2 (2014-06-08)
|
||||||
|
* BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
|
||||||
|
* BF: Fixed accidental BC break from v0.3.1
|
||||||
|
* Added autoDelete parameter to Topic to destroy when empty of connections
|
||||||
|
* Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App)
|
||||||
|
* Normalized Exceptions in WAMP
|
||||||
|
|
||||||
|
* 0.3.1 (2014-05-26)
|
||||||
|
* Added query parameter support to Router, set in HTTP request (ws://server?hello=world)
|
||||||
|
* HHVM compatibility
|
||||||
|
* BF: React/0.4 support; CPU starvation bug fixes
|
||||||
|
* BF: Allow App::route to ignore Host header
|
||||||
|
* Added expected filters to WAMP Topic broadcast method
|
||||||
|
* Resource cleanup in WAMP TopicManager
|
||||||
|
|
||||||
|
* 0.3.0 (2013-10-14)
|
||||||
|
* Added the `App` class to help making Ratchet so easy to use it's silly
|
||||||
|
* BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks
|
||||||
|
* Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router
|
||||||
|
* BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer
|
||||||
|
* BF: Single sub-protocol selection to conform with RFC6455
|
||||||
|
* BF: Sanity checks on WAMP protocol to prevent errors
|
||||||
|
|
||||||
|
* 0.2.8 (2013-09-19)
|
||||||
|
* React 0.3 support
|
||||||
|
|
||||||
|
* 0.2.7 (2013-06-09)
|
||||||
|
* BF: Sub-protocol negotation with Guzzle 3.6
|
||||||
|
|
||||||
|
* 0.2.6 (2013-06-01)
|
||||||
|
* Guzzle 3.6 support
|
||||||
|
|
||||||
|
* 0.2.5 (2013-04-01)
|
||||||
|
* Fixed Hixie-76 handshake bug
|
||||||
|
|
||||||
|
* 0.2.4 (2013-03-09)
|
||||||
|
* Support for Symfony 2.2 and Guzzle 2.3
|
||||||
|
* Minor bug fixes when handling errors
|
||||||
|
|
||||||
|
* 0.2.3 (2012-11-21)
|
||||||
|
* Bumped dep: Guzzle to v3, React to v0.2.4
|
||||||
|
* More tests
|
||||||
|
|
||||||
|
* 0.2.2 (2012-10-20)
|
||||||
|
* Bumped deps to use React v0.2
|
||||||
|
|
||||||
|
* 0.2.1 (2012-10-13)
|
||||||
|
* BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string)
|
||||||
|
* Documentation corrections
|
||||||
|
* Using new composer structure
|
||||||
|
|
||||||
|
* 0.2 (2012-09-07)
|
||||||
|
* Ratchet passes every non-binary-frame test from the Autobahn Testsuite
|
||||||
|
* Major performance improvements
|
||||||
|
* BC: Renamed "WampServer" to "ServerProtocol"
|
||||||
|
* BC: New "WampServer" component passes Topic container objects of subscribed Connections
|
||||||
|
* Option to turn off UTF-8 checks in order to increase performance
|
||||||
|
* Switched dependency guzzle/guzzle to guzzle/http (no API changes)
|
||||||
|
* mbstring no longer required
|
||||||
|
|
||||||
|
* 0.1.5 (2012-07-12)
|
||||||
|
* BF: Error where service wouldn't run on PHP <= 5.3.8
|
||||||
|
* Dependency library updates
|
||||||
|
|
||||||
|
* 0.1.4 (2012-06-17)
|
||||||
|
* Fixed dozens of failing AB tests
|
||||||
|
* BF: Proper socket buffer handling
|
||||||
|
|
||||||
|
* 0.1.3 (2012-06-15)
|
||||||
|
* Major refactor inside WebSocket protocol handling, more loosley coupled
|
||||||
|
* BF: Proper error handling on failed WebSocket connections
|
||||||
|
* BF: Handle TCP message concatenation
|
||||||
|
* Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance
|
||||||
|
* mb_string now a requirement
|
||||||
|
|
||||||
|
* 0.1.2 (2012-05-19)
|
||||||
|
* BC/BF: Updated WAMP API to coincide with the official spec
|
||||||
|
* Tweaks to improve running as a long lived process
|
||||||
|
|
||||||
|
* 0.1.1 (2012-05-14)
|
||||||
|
* Separated interfaces allowing WebSockets to support multiple sub protocols
|
||||||
|
* BF: remoteAddress variable on connections returns proper value
|
||||||
|
|
||||||
|
* 0.1 (2012-05-11)
|
||||||
|
* First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList
|
||||||
|
* I/O now handled by React, making Ratchet fully asynchronous
|
19
objects/cboden/ratchet/LICENSE
Normal file
19
objects/cboden/ratchet/LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2011-2020 Chris Boden
|
||||||
|
|
||||||
|
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.
|
42
objects/cboden/ratchet/Makefile
Normal file
42
objects/cboden/ratchet/Makefile
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# This file is intended to ease the author's development and testing process
|
||||||
|
# Users do not need to use `make`; Ratchet does not need to be compiled
|
||||||
|
|
||||||
|
test:
|
||||||
|
vendor/bin/phpunit
|
||||||
|
|
||||||
|
cover:
|
||||||
|
vendor/bin/phpunit --coverage-text --coverage-html=reports/coverage
|
||||||
|
|
||||||
|
abtests:
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8001 LibEvent &
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8002 StreamSelect &
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8004 LibEv &
|
||||||
|
wstest -m testeeserver -w ws://localhost:8000 &
|
||||||
|
sleep 1
|
||||||
|
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json
|
||||||
|
killall php wstest
|
||||||
|
|
||||||
|
abtest:
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect &
|
||||||
|
sleep 1
|
||||||
|
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json
|
||||||
|
killall php
|
||||||
|
|
||||||
|
profile:
|
||||||
|
php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent &
|
||||||
|
sleep 1
|
||||||
|
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json
|
||||||
|
killall php
|
||||||
|
|
||||||
|
apidocs:
|
||||||
|
apigen --title Ratchet -d reports/api \
|
||||||
|
-s src/ \
|
||||||
|
-s vendor/ratchet/rfc6455/src \
|
||||||
|
-s vendor/react/event-loop/src \
|
||||||
|
-s vendor/react/socket/src \
|
||||||
|
-s vendor/react/stream/src \
|
||||||
|
-s vendor/psr/http-message/src \
|
||||||
|
-s vendor/symfony/http-foundation/Session \
|
||||||
|
-s vendor/symfony/routing \
|
||||||
|
-s vendor/evenement/evenement/src/Evenement \
|
||||||
|
--exclude=vendor/symfony/routing/Tests \
|
83
objects/cboden/ratchet/README.md
Normal file
83
objects/cboden/ratchet/README.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# Ratchet
|
||||||
|
|
||||||
|
[](http://travis-ci.org/ratchetphp/Ratchet)
|
||||||
|
[](http://socketo.me/reports/ab/index.html)
|
||||||
|
[](https://packagist.org/packages/cboden/ratchet)
|
||||||
|
|
||||||
|
A PHP library for asynchronously serving WebSockets.
|
||||||
|
Build up your application through simple interfaces and re-use your application without changing any of its code just by combining different components.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Shell access is required and root access is recommended.
|
||||||
|
To avoid proxy/firewall blockage it's recommended WebSockets are requested on port 80 or 443 (SSL), which requires root access.
|
||||||
|
In order to do this, along with your sync web stack, you can either use a reverse proxy or two separate machines.
|
||||||
|
You can find more details in the [server conf docs](http://socketo.me/docs/deploy#server_configuration).
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
User and API documentation is available on Ratchet's website: http://socketo.me
|
||||||
|
|
||||||
|
See https://github.com/cboden/Ratchet-examples for some out-of-the-box working demos using Ratchet.
|
||||||
|
|
||||||
|
Need help? Have a question? Want to provide feedback? Write a message on the [Google Groups Mailing List](https://groups.google.com/forum/#!forum/ratchet-php).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### A quick example
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
// Make sure composer dependencies have been installed
|
||||||
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* chat.php
|
||||||
|
* Send any incoming messages to all connected clients (except sender)
|
||||||
|
*/
|
||||||
|
class MyChat implements MessageComponentInterface {
|
||||||
|
protected $clients;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->clients = new \SplObjectStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->clients->attach($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
foreach ($this->clients as $client) {
|
||||||
|
if ($from != $client) {
|
||||||
|
$client->send($msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->clients->detach($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the server application through the WebSocket protocol on port 8080
|
||||||
|
$app = new Ratchet\App('localhost', 8080);
|
||||||
|
$app->route('/chat', new MyChat, array('*'));
|
||||||
|
$app->route('/echo', new Ratchet\Server\EchoServer, array('*'));
|
||||||
|
$app->run();
|
||||||
|
```
|
||||||
|
|
||||||
|
$ php chat.php
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Then some JavaScript in the browser:
|
||||||
|
var conn = new WebSocket('ws://localhost:8080/echo');
|
||||||
|
conn.onmessage = function(e) { console.log(e.data); };
|
||||||
|
conn.onopen = function(e) { conn.send('Hello Me!'); };
|
||||||
|
```
|
39
objects/cboden/ratchet/composer.json
Normal file
39
objects/cboden/ratchet/composer.json
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"name": "cboden/ratchet"
|
||||||
|
, "type": "library"
|
||||||
|
, "description": "PHP WebSocket library"
|
||||||
|
, "keywords": ["WebSockets", "Server", "Ratchet", "Sockets", "WebSocket"]
|
||||||
|
, "homepage": "http://socketo.me"
|
||||||
|
, "license": "MIT"
|
||||||
|
, "authors": [
|
||||||
|
{
|
||||||
|
"name": "Chris Boden"
|
||||||
|
, "email": "cboden@gmail.com"
|
||||||
|
, "role": "Developer"
|
||||||
|
}
|
||||||
|
, {
|
||||||
|
"name": "Matt Bonneau"
|
||||||
|
, "role": "Developer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, "support": {
|
||||||
|
"issues": "https://github.com/ratchetphp/Ratchet/issues"
|
||||||
|
, "chat": "https://gitter.im/reactphp/reactphp"
|
||||||
|
}
|
||||||
|
, "autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Ratchet\\": "src/Ratchet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, "require": {
|
||||||
|
"php": ">=5.4.2"
|
||||||
|
, "ratchet/rfc6455": "^0.3"
|
||||||
|
, "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5"
|
||||||
|
, "guzzlehttp/psr7": "^1.0"
|
||||||
|
, "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0"
|
||||||
|
, "symfony/routing": "^2.6|^3.0|^4.0|^5.0"
|
||||||
|
}
|
||||||
|
, "require-dev": {
|
||||||
|
"phpunit/phpunit": "~4.8"
|
||||||
|
}
|
||||||
|
}
|
30
objects/cboden/ratchet/phpunit.xml.dist
Normal file
30
objects/cboden/ratchet/phpunit.xml.dist
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
forceCoversAnnotation="true"
|
||||||
|
mapTestClassNameToCoveredClassName="true"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
colors="true"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
syntaxCheck="false"
|
||||||
|
stopOnError="false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit">
|
||||||
|
<directory>./tests/unit/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="integration">
|
||||||
|
<directory>./tests/integration/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./src/</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps ConnectionInterface objects via the decorator pattern but allows
|
||||||
|
* parameters to bubble through with magic methods
|
||||||
|
* @todo It sure would be nice if I could make most of this a trait...
|
||||||
|
*/
|
||||||
|
abstract class AbstractConnectionDecorator implements ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* @var ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $wrappedConn;
|
||||||
|
|
||||||
|
public function __construct(ConnectionInterface $conn) {
|
||||||
|
$this->wrappedConn = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected function getConnection() {
|
||||||
|
return $this->wrappedConn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($name, $value) {
|
||||||
|
$this->wrappedConn->$name = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name) {
|
||||||
|
return $this->wrappedConn->$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($name) {
|
||||||
|
return isset($this->wrappedConn->$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unset($name) {
|
||||||
|
unset($this->wrappedConn->$name);
|
||||||
|
}
|
||||||
|
}
|
146
objects/cboden/ratchet/src/Ratchet/App.php
Normal file
146
objects/cboden/ratchet/src/Ratchet/App.php
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\EventLoop\Factory as LoopFactory;
|
||||||
|
use React\Socket\Server as Reactor;
|
||||||
|
use React\Socket\SecureServer as SecureReactor;
|
||||||
|
use Ratchet\Http\HttpServerInterface;
|
||||||
|
use Ratchet\Http\OriginCheck;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
use Ratchet\Server\IoServer;
|
||||||
|
use Ratchet\Server\FlashPolicy;
|
||||||
|
use Ratchet\Http\HttpServer;
|
||||||
|
use Ratchet\Http\Router;
|
||||||
|
use Ratchet\WebSocket\MessageComponentInterface as WsMessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServer;
|
||||||
|
use Ratchet\Wamp\WampServer;
|
||||||
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An opinionated facade class to quickly and easily create a WebSocket server.
|
||||||
|
* A few configuration assumptions are made and some best-practice security conventions are applied by default.
|
||||||
|
*/
|
||||||
|
class App {
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\Routing\RouteCollection
|
||||||
|
*/
|
||||||
|
public $routes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Server\IoServer
|
||||||
|
*/
|
||||||
|
public $flashServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Server\IoServer
|
||||||
|
*/
|
||||||
|
protected $_server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Host passed in construct used for same origin policy
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $httpHost;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The port the socket is listening
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $_routeCounter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $httpHost HTTP hostname clients intend to connect to. MUST match JS `new WebSocket('ws://$httpHost');`
|
||||||
|
* @param int $port Port to listen on. If 80, assuming production, Flash on 843 otherwise expecting Flash to be proxied through 8843
|
||||||
|
* @param string $address IP address to bind to. Default is localhost/proxy only. '0.0.0.0' for any machine.
|
||||||
|
* @param LoopInterface $loop Specific React\EventLoop to bind the application to. null will create one for you.
|
||||||
|
*/
|
||||||
|
public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null) {
|
||||||
|
if (extension_loaded('xdebug') && getenv('RATCHET_DISABLE_XDEBUG_WARN') === false) {
|
||||||
|
trigger_error('XDebug extension detected. Remember to disable this if performance testing or going live!', E_USER_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $loop) {
|
||||||
|
$loop = LoopFactory::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->httpHost = $httpHost;
|
||||||
|
$this->port = $port;
|
||||||
|
|
||||||
|
$socket = new Reactor($address . ':' . $port, $loop);
|
||||||
|
|
||||||
|
$this->routes = new RouteCollection;
|
||||||
|
$this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop);
|
||||||
|
|
||||||
|
$policy = new FlashPolicy;
|
||||||
|
$policy->addAllowedAccess($httpHost, 80);
|
||||||
|
$policy->addAllowedAccess($httpHost, $port);
|
||||||
|
|
||||||
|
if (80 == $port) {
|
||||||
|
$flashUri = '0.0.0.0:843';
|
||||||
|
} else {
|
||||||
|
$flashUri = 8843;
|
||||||
|
}
|
||||||
|
$flashSock = new Reactor($flashUri, $loop);
|
||||||
|
$this->flashServer = new IoServer($policy, $flashSock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an endpoint/application to the server
|
||||||
|
* @param string $path The URI the client will connect to
|
||||||
|
* @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket
|
||||||
|
* @param array $allowedOrigins An array of hosts allowed to connect (same host by default), ['*'] for any
|
||||||
|
* @param string $httpHost Override the $httpHost variable provided in the __construct
|
||||||
|
* @return ComponentInterface|WsServer
|
||||||
|
*/
|
||||||
|
public function route($path, ComponentInterface $controller, array $allowedOrigins = array(), $httpHost = null) {
|
||||||
|
if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) {
|
||||||
|
$decorated = $controller;
|
||||||
|
} elseif ($controller instanceof WampServerInterface) {
|
||||||
|
$decorated = new WsServer(new WampServer($controller));
|
||||||
|
$decorated->enableKeepAlive($this->_server->loop);
|
||||||
|
} elseif ($controller instanceof MessageComponentInterface || $controller instanceof WsMessageComponentInterface) {
|
||||||
|
$decorated = new WsServer($controller);
|
||||||
|
$decorated->enableKeepAlive($this->_server->loop);
|
||||||
|
} else {
|
||||||
|
$decorated = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($httpHost === null) {
|
||||||
|
$httpHost = $this->httpHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedOrigins = array_values($allowedOrigins);
|
||||||
|
if (0 === count($allowedOrigins)) {
|
||||||
|
$allowedOrigins[] = $httpHost;
|
||||||
|
}
|
||||||
|
if ('*' !== $allowedOrigins[0]) {
|
||||||
|
$decorated = new OriginCheck($decorated, $allowedOrigins);
|
||||||
|
}
|
||||||
|
|
||||||
|
//allow origins in flash policy server
|
||||||
|
if(empty($this->flashServer) === false) {
|
||||||
|
foreach($allowedOrigins as $allowedOrgin) {
|
||||||
|
$this->flashServer->app->addAllowedAccess($allowedOrgin, $this->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost, array(), array('GET')));
|
||||||
|
|
||||||
|
return $decorated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the server by entering the event loop
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
$this->_server->run();
|
||||||
|
}
|
||||||
|
}
|
31
objects/cboden/ratchet/src/Ratchet/ComponentInterface.php
Normal file
31
objects/cboden/ratchet/src/Ratchet/ComponentInterface.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the interface to build a Ratchet application with.
|
||||||
|
* It implements the decorator pattern to build an application stack
|
||||||
|
*/
|
||||||
|
interface ComponentInterface {
|
||||||
|
/**
|
||||||
|
* When a new connection is opened it will be passed to this method
|
||||||
|
* @param ConnectionInterface $conn The socket/connection that just connected to your application
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onOpen(ConnectionInterface $conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed.
|
||||||
|
* @param ConnectionInterface $conn The socket/connection that is closing/closed
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown,
|
||||||
|
* the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method
|
||||||
|
* @param ConnectionInterface $conn
|
||||||
|
* @param \Exception $e
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e);
|
||||||
|
}
|
26
objects/cboden/ratchet/src/Ratchet/ConnectionInterface.php
Normal file
26
objects/cboden/ratchet/src/Ratchet/ConnectionInterface.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of Ratchet being used
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const VERSION = 'Ratchet/0.4.3';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy object representing a connection to the application
|
||||||
|
* This acts as a container to store data (in memory) about the connection
|
||||||
|
*/
|
||||||
|
interface ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* Send data to the connection
|
||||||
|
* @param string $data
|
||||||
|
* @return \Ratchet\ConnectionInterface
|
||||||
|
*/
|
||||||
|
function send($data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection
|
||||||
|
*/
|
||||||
|
function close();
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use GuzzleHttp\Psr7 as gPsr;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
|
||||||
|
trait CloseResponseTrait {
|
||||||
|
/**
|
||||||
|
* Close a connection with an HTTP response
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param int $code HTTP status code
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
private function close(ConnectionInterface $conn, $code = 400, array $additional_headers = []) {
|
||||||
|
$response = new Response($code, array_merge([
|
||||||
|
'X-Powered-By' => \Ratchet\VERSION
|
||||||
|
], $additional_headers));
|
||||||
|
|
||||||
|
$conn->send(gPsr\str($response));
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\MessageInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use GuzzleHttp\Psr7 as gPsr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class receives streaming data from a client request
|
||||||
|
* and parses HTTP headers, returning a PSR-7 Request object
|
||||||
|
* once it's been buffered
|
||||||
|
*/
|
||||||
|
class HttpRequestParser implements MessageInterface {
|
||||||
|
const EOM = "\r\n\r\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of bytes the request can be
|
||||||
|
* This is a security measure to prevent attacks
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $maxSize = 4096;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\ConnectionInterface $context
|
||||||
|
* @param string $data Data stream to buffer
|
||||||
|
* @return \Psr\Http\Message\RequestInterface
|
||||||
|
* @throws \OverflowException If the message buffer has become too large
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $context, $data) {
|
||||||
|
if (!isset($context->httpBuffer)) {
|
||||||
|
$context->httpBuffer = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$context->httpBuffer .= $data;
|
||||||
|
|
||||||
|
if (strlen($context->httpBuffer) > (int)$this->maxSize) {
|
||||||
|
throw new \OverflowException("Maximum buffer size of {$this->maxSize} exceeded parsing HTTP header");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isEom($context->httpBuffer)) {
|
||||||
|
$request = $this->parse($context->httpBuffer);
|
||||||
|
|
||||||
|
unset($context->httpBuffer);
|
||||||
|
|
||||||
|
return $request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the message has been buffered as per the HTTP specification
|
||||||
|
* @param string $message
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isEom($message) {
|
||||||
|
return (boolean)strpos($message, static::EOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $headers
|
||||||
|
* @return \Psr\Http\Message\RequestInterface
|
||||||
|
*/
|
||||||
|
public function parse($headers) {
|
||||||
|
return gPsr\parse_request($headers);
|
||||||
|
}
|
||||||
|
}
|
76
objects/cboden/ratchet/src/Ratchet/Http/HttpServer.php
Normal file
76
objects/cboden/ratchet/src/Ratchet/Http/HttpServer.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class HttpServer implements MessageComponentInterface {
|
||||||
|
use CloseResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffers incoming HTTP requests returning a Guzzle Request when coalesced
|
||||||
|
* @var HttpRequestParser
|
||||||
|
* @note May not expose this in the future, may do through facade methods
|
||||||
|
*/
|
||||||
|
protected $_reqParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Http\HttpServerInterface
|
||||||
|
*/
|
||||||
|
protected $_httpServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HttpServerInterface
|
||||||
|
*/
|
||||||
|
public function __construct(HttpServerInterface $component) {
|
||||||
|
$this->_httpServer = $component;
|
||||||
|
$this->_reqParser = new HttpRequestParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$conn->httpHeadersReceived = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
if (true !== $from->httpHeadersReceived) {
|
||||||
|
try {
|
||||||
|
if (null === ($request = $this->_reqParser->onMessage($from, $msg))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (\OverflowException $oe) {
|
||||||
|
return $this->close($from, 413);
|
||||||
|
}
|
||||||
|
|
||||||
|
$from->httpHeadersReceived = true;
|
||||||
|
|
||||||
|
return $this->_httpServer->onOpen($from, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_httpServer->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
if ($conn->httpHeadersReceived) {
|
||||||
|
$this->_httpServer->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
if ($conn->httpHeadersReceived) {
|
||||||
|
$this->_httpServer->onError($conn, $e);
|
||||||
|
} else {
|
||||||
|
$this->close($conn, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
interface HttpServerInterface extends MessageComponentInterface {
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param \Psr\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!!
|
||||||
|
* @throws \UnexpectedValueException if a RequestInterface is not passed
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class NoOpHttpServerController implements HttpServerInterface {
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
}
|
||||||
|
}
|
65
objects/cboden/ratchet/src/Ratchet/Http/OriginCheck.php
Normal file
65
objects/cboden/ratchet/src/Ratchet/Http/OriginCheck.php
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A middleware to ensure JavaScript clients connecting are from the expected domain.
|
||||||
|
* This protects other websites from open WebSocket connections to your application.
|
||||||
|
* Note: This can be spoofed from non-web browser clients
|
||||||
|
*/
|
||||||
|
class OriginCheck implements HttpServerInterface {
|
||||||
|
use CloseResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
protected $_component;
|
||||||
|
|
||||||
|
public $allowedOrigins = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param MessageComponentInterface $component Component/Application to decorate
|
||||||
|
* @param array $allowed An array of allowed domains that are allowed to connect from
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $component, array $allowed = []) {
|
||||||
|
$this->_component = $component;
|
||||||
|
$this->allowedOrigins += $allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
$header = (string)$request->getHeader('Origin')[0];
|
||||||
|
$origin = parse_url($header, PHP_URL_HOST) ?: $header;
|
||||||
|
|
||||||
|
if (!in_array($origin, $this->allowedOrigins)) {
|
||||||
|
return $this->close($conn, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_component->onOpen($conn, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
return $this->_component->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn) {
|
||||||
|
return $this->_component->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_component->onError($conn, $e);
|
||||||
|
}
|
||||||
|
}
|
96
objects/cboden/ratchet/src/Ratchet/Http/Router.php
Normal file
96
objects/cboden/ratchet/src/Ratchet/Http/Router.php
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||||
|
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||||
|
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||||
|
use GuzzleHttp\Psr7 as gPsr;
|
||||||
|
|
||||||
|
class Router implements HttpServerInterface {
|
||||||
|
use CloseResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
|
||||||
|
*/
|
||||||
|
protected $_matcher;
|
||||||
|
|
||||||
|
private $_noopController;
|
||||||
|
|
||||||
|
public function __construct(UrlMatcherInterface $matcher) {
|
||||||
|
$this->_matcher = $matcher;
|
||||||
|
$this->_noopController = new NoOpHttpServerController;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @throws \UnexpectedValueException If a controller is not \Ratchet\Http\HttpServerInterface
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
if (null === $request) {
|
||||||
|
throw new \UnexpectedValueException('$request can not be null');
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->controller = $this->_noopController;
|
||||||
|
|
||||||
|
$uri = $request->getUri();
|
||||||
|
|
||||||
|
$context = $this->_matcher->getContext();
|
||||||
|
$context->setMethod($request->getMethod());
|
||||||
|
$context->setHost($uri->getHost());
|
||||||
|
|
||||||
|
try {
|
||||||
|
$route = $this->_matcher->match($uri->getPath());
|
||||||
|
} catch (MethodNotAllowedException $nae) {
|
||||||
|
return $this->close($conn, 405, array('Allow' => $nae->getAllowedMethods()));
|
||||||
|
} catch (ResourceNotFoundException $nfe) {
|
||||||
|
return $this->close($conn, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($route['_controller']) && class_exists($route['_controller'])) {
|
||||||
|
$route['_controller'] = new $route['_controller'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($route['_controller'] instanceof HttpServerInterface)) {
|
||||||
|
throw new \UnexpectedValueException('All routes must implement Ratchet\Http\HttpServerInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters = [];
|
||||||
|
foreach($route as $key => $value) {
|
||||||
|
if ((is_string($key)) && ('_' !== substr($key, 0, 1))) {
|
||||||
|
$parameters[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$parameters = array_merge($parameters, gPsr\parse_query($uri->getQuery() ?: ''));
|
||||||
|
|
||||||
|
$request = $request->withUri($uri->withQuery(gPsr\build_query($parameters)));
|
||||||
|
|
||||||
|
$conn->controller = $route['_controller'];
|
||||||
|
$conn->controller->onOpen($conn, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$from->controller->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
if (isset($conn->controller)) {
|
||||||
|
$conn->controller->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
if (isset($conn->controller)) {
|
||||||
|
$conn->controller->onError($conn, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
interface MessageComponentInterface extends ComponentInterface, MessageInterface {
|
||||||
|
}
|
12
objects/cboden/ratchet/src/Ratchet/MessageInterface.php
Normal file
12
objects/cboden/ratchet/src/Ratchet/MessageInterface.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
interface MessageInterface {
|
||||||
|
/**
|
||||||
|
* Triggered when a client sends data through the socket
|
||||||
|
* @param \Ratchet\ConnectionInterface $from The socket/connection that sent the message to your application
|
||||||
|
* @param string $msg The message received
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg);
|
||||||
|
}
|
23
objects/cboden/ratchet/src/Ratchet/Server/EchoServer.php
Normal file
23
objects/cboden/ratchet/src/Ratchet/Server/EchoServer.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple Ratchet application that will reply to all messages with the message it received
|
||||||
|
*/
|
||||||
|
class EchoServer implements MessageComponentInterface {
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$from->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
200
objects/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php
Normal file
200
objects/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An app to go on a server stack to pass a policy file to a Flash socket
|
||||||
|
* Useful if you're using Flash as a WebSocket polyfill on IE
|
||||||
|
* Be sure to run your server instance on port 843
|
||||||
|
* By default this lets accepts everything, make sure you tighten the rules up for production
|
||||||
|
* @final
|
||||||
|
* @link http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
|
||||||
|
* @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1
|
||||||
|
* @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
|
||||||
|
*/
|
||||||
|
class FlashPolicy implements MessageComponentInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the root policy node
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_policy = '<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy></cross-domain-policy>';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an array of allowed domains and their ports
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $_access = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_siteControl = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_cache = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_cacheValid = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a domain to an allowed access list.
|
||||||
|
*
|
||||||
|
* @param string $domain Specifies a requesting domain to be granted access. Both named domains and IP
|
||||||
|
* addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can
|
||||||
|
* be used to match all domains when used alone, or multiple domains (subdomains) when used as a
|
||||||
|
* prefix for an explicit, second-level domain name separated with a dot (.)
|
||||||
|
* @param string $ports A comma-separated list of ports or range of ports that a socket connection
|
||||||
|
* is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers.
|
||||||
|
* Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
|
||||||
|
* be used to allow all ports.
|
||||||
|
* @param bool $secure
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @return FlashPolicy
|
||||||
|
*/
|
||||||
|
public function addAllowedAccess($domain, $ports = '*', $secure = false) {
|
||||||
|
if (!$this->validateDomain($domain)) {
|
||||||
|
throw new \UnexpectedValueException('Invalid domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->validatePorts($ports)) {
|
||||||
|
throw new \UnexpectedValueException('Invalid Port');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_access[] = array($domain, $ports, (boolean)$secure);
|
||||||
|
$this->_cacheValid = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all domains from the allowed access list.
|
||||||
|
*
|
||||||
|
* @return \Ratchet\Server\FlashPolicy
|
||||||
|
*/
|
||||||
|
public function clearAllowedAccess() {
|
||||||
|
$this->_access = array();
|
||||||
|
$this->_cacheValid = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable
|
||||||
|
* domain policy files other than the master policy file located in the target domain's root and named
|
||||||
|
* crossdomain.xml.
|
||||||
|
*
|
||||||
|
* @param string $permittedCrossDomainPolicies
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @return FlashPolicy
|
||||||
|
*/
|
||||||
|
public function setSiteControl($permittedCrossDomainPolicies = 'all') {
|
||||||
|
if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
|
||||||
|
throw new \UnexpectedValueException('Invalid site control set');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_siteControl = $permittedCrossDomainPolicies;
|
||||||
|
$this->_cacheValid = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
if (!$this->_cacheValid) {
|
||||||
|
$this->_cache = $this->renderPolicy()->asXML();
|
||||||
|
$this->_cacheValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$from->send($this->_cache . "\0");
|
||||||
|
$from->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the crossdomain file based on the template policy
|
||||||
|
*
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @return \SimpleXMLElement
|
||||||
|
*/
|
||||||
|
public function renderPolicy() {
|
||||||
|
$policy = new \SimpleXMLElement($this->_policy);
|
||||||
|
|
||||||
|
$siteControl = $policy->addChild('site-control');
|
||||||
|
|
||||||
|
if ($this->_siteControl == '') {
|
||||||
|
$this->setSiteControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
$siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl);
|
||||||
|
|
||||||
|
if (empty($this->_access)) {
|
||||||
|
throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->_access as $access) {
|
||||||
|
$tmp = $policy->addChild('allow-access-from');
|
||||||
|
$tmp->addAttribute('domain', $access[0]);
|
||||||
|
$tmp->addAttribute('to-ports', $access[1]);
|
||||||
|
$tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the proper site control was passed
|
||||||
|
*
|
||||||
|
* @param string $permittedCrossDomainPolicies
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validateSiteControl($permittedCrossDomainPolicies) {
|
||||||
|
//'by-content-type' and 'by-ftp-filename' are not available for sockets
|
||||||
|
return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate for proper domains (wildcards allowed)
|
||||||
|
*
|
||||||
|
* @param string $domain
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validateDomain($domain) {
|
||||||
|
return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure valid ports were passed
|
||||||
|
*
|
||||||
|
* @param string $port
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validatePorts($port) {
|
||||||
|
return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port);
|
||||||
|
}
|
||||||
|
}
|
38
objects/cboden/ratchet/src/Ratchet/Server/IoConnection.php
Normal file
38
objects/cboden/ratchet/src/Ratchet/Server/IoConnection.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use React\Socket\ConnectionInterface as ReactConn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
class IoConnection implements ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* @var \React\Socket\ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $conn;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function __construct(ReactConn $conn) {
|
||||||
|
$this->conn = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function send($data) {
|
||||||
|
$this->conn->write($data);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close() {
|
||||||
|
$this->conn->end();
|
||||||
|
}
|
||||||
|
}
|
140
objects/cboden/ratchet/src/Ratchet/Server/IoServer.php
Normal file
140
objects/cboden/ratchet/src/Ratchet/Server/IoServer.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Socket\ServerInterface;
|
||||||
|
use React\EventLoop\Factory as LoopFactory;
|
||||||
|
use React\Socket\Server as Reactor;
|
||||||
|
use React\Socket\SecureServer as SecureReactor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an open-ended socket to listen on a port for incoming connections.
|
||||||
|
* Events are delegated through this to attached applications
|
||||||
|
*/
|
||||||
|
class IoServer {
|
||||||
|
/**
|
||||||
|
* @var \React\EventLoop\LoopInterface
|
||||||
|
*/
|
||||||
|
public $loop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
public $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The socket server the Ratchet Application is run off of
|
||||||
|
* @var \React\Socket\ServerInterface
|
||||||
|
*/
|
||||||
|
public $socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\MessageComponentInterface $app The Ratchet application stack to host
|
||||||
|
* @param \React\Socket\ServerInterface $socket The React socket server to run the Ratchet application off of
|
||||||
|
* @param \React\EventLoop\LoopInterface|null $loop The React looper to run the Ratchet application off of
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $app, ServerInterface $socket, LoopInterface $loop = null) {
|
||||||
|
if (false === strpos(PHP_VERSION, "hiphop")) {
|
||||||
|
gc_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
ob_implicit_flush();
|
||||||
|
|
||||||
|
$this->loop = $loop;
|
||||||
|
$this->app = $app;
|
||||||
|
$this->socket = $socket;
|
||||||
|
|
||||||
|
$socket->on('connection', array($this, 'handleConnect'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\MessageComponentInterface $component The application that I/O will call when events are received
|
||||||
|
* @param int $port The port to server sockets on
|
||||||
|
* @param string $address The address to receive sockets on (0.0.0.0 means receive connections from any)
|
||||||
|
* @return IoServer
|
||||||
|
*/
|
||||||
|
public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') {
|
||||||
|
$loop = LoopFactory::create();
|
||||||
|
$socket = new Reactor($address . ':' . $port, $loop);
|
||||||
|
|
||||||
|
return new static($component, $socket, $loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the application by entering the event loop
|
||||||
|
* @throws \RuntimeException If a loop was not previously specified
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
if (null === $this->loop) {
|
||||||
|
throw new \RuntimeException("A React Loop was not provided during instantiation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
$this->loop->run();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when a new connection is received from React
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleConnect($conn) {
|
||||||
|
$conn->decor = new IoConnection($conn);
|
||||||
|
$conn->decor->resourceId = (int)$conn->stream;
|
||||||
|
|
||||||
|
$uri = $conn->getRemoteAddress();
|
||||||
|
$conn->decor->remoteAddress = trim(
|
||||||
|
parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_HOST),
|
||||||
|
'[]'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app->onOpen($conn->decor);
|
||||||
|
|
||||||
|
$conn->on('data', function ($data) use ($conn) {
|
||||||
|
$this->handleData($data, $conn);
|
||||||
|
});
|
||||||
|
$conn->on('close', function () use ($conn) {
|
||||||
|
$this->handleEnd($conn);
|
||||||
|
});
|
||||||
|
$conn->on('error', function (\Exception $e) use ($conn) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data has been received from React
|
||||||
|
* @param string $data
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleData($data, $conn) {
|
||||||
|
try {
|
||||||
|
$this->app->onMessage($conn->decor, $data);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A connection has been closed by React
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleEnd($conn) {
|
||||||
|
try {
|
||||||
|
$this->app->onClose($conn->decor);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($conn->decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error has occurred, let the listening application know
|
||||||
|
* @param \Exception $e
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleError(\Exception $e, $conn) {
|
||||||
|
$this->app->onError($conn->decor, $e);
|
||||||
|
}
|
||||||
|
}
|
111
objects/cboden/ratchet/src/Ratchet/Server/IpBlackList.php
Normal file
111
objects/cboden/ratchet/src/Ratchet/Server/IpBlackList.php
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class IpBlackList implements MessageComponentInterface {
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $_blacklist = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
protected $_decorating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\MessageComponentInterface $component
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $component) {
|
||||||
|
$this->_decorating = $component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an address to the blacklist that will not be allowed to connect to your application
|
||||||
|
* @param string $ip IP address to block from connecting to your application
|
||||||
|
* @return IpBlackList
|
||||||
|
*/
|
||||||
|
public function blockAddress($ip) {
|
||||||
|
$this->_blacklist[$ip] = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock an address so they can access your application again
|
||||||
|
* @param string $ip IP address to unblock from connecting to your application
|
||||||
|
* @return IpBlackList
|
||||||
|
*/
|
||||||
|
public function unblockAddress($ip) {
|
||||||
|
if (isset($this->_blacklist[$this->filterAddress($ip)])) {
|
||||||
|
unset($this->_blacklist[$this->filterAddress($ip)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $address
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isBlocked($address) {
|
||||||
|
return (isset($this->_blacklist[$this->filterAddress($address)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of all the addresses blocked
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getBlockedAddresses() {
|
||||||
|
return array_keys($this->_blacklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $address
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function filterAddress($address) {
|
||||||
|
if (strstr($address, ':') && substr_count($address, '.') == 3) {
|
||||||
|
list($address, $port) = explode(':', $address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onOpen(ConnectionInterface $conn) {
|
||||||
|
if ($this->isBlocked($conn->remoteAddress)) {
|
||||||
|
return $conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_decorating->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
return $this->_decorating->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn) {
|
||||||
|
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||||
|
$this->_decorating->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||||
|
$this->_decorating->onError($conn, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
|
interface HandlerInterface {
|
||||||
|
/**
|
||||||
|
* @param array
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function serialize(array $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function unserialize($raw);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
|
class PhpBinaryHandler implements HandlerInterface {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function serialize(array $data) {
|
||||||
|
throw new \RuntimeException("Serialize PhpHandler:serialize code not written yet, write me!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
|
||||||
|
*/
|
||||||
|
public function unserialize($raw) {
|
||||||
|
$returnData = array();
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
while ($offset < strlen($raw)) {
|
||||||
|
$num = ord($raw[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
$varname = substr($raw, $offset, $num);
|
||||||
|
$offset += $num;
|
||||||
|
$data = unserialize(substr($raw, $offset));
|
||||||
|
|
||||||
|
$returnData[$varname] = $data;
|
||||||
|
$offset += strlen(serialize($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
|
class PhpHandler implements HandlerInterface {
|
||||||
|
/**
|
||||||
|
* Simply reverse behaviour of unserialize method.
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function serialize(array $data) {
|
||||||
|
$preSerialized = array();
|
||||||
|
$serialized = '';
|
||||||
|
|
||||||
|
if (count($data)) {
|
||||||
|
foreach ($data as $bucket => $bucketData) {
|
||||||
|
$preSerialized[] = $bucket . '|' . serialize($bucketData);
|
||||||
|
}
|
||||||
|
$serialized = implode('', $preSerialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
|
||||||
|
* @throws \UnexpectedValueException If there is a problem parsing the data
|
||||||
|
*/
|
||||||
|
public function unserialize($raw) {
|
||||||
|
$returnData = array();
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
while ($offset < strlen($raw)) {
|
||||||
|
if (!strstr(substr($raw, $offset), "|")) {
|
||||||
|
throw new \UnexpectedValueException("invalid data, remaining: " . substr($raw, $offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = strpos($raw, "|", $offset);
|
||||||
|
$num = $pos - $offset;
|
||||||
|
$varname = substr($raw, $offset, $num);
|
||||||
|
$offset += $num + 1;
|
||||||
|
$data = unserialize(substr($raw, $offset));
|
||||||
|
|
||||||
|
$returnData[$varname] = $data;
|
||||||
|
$offset += strlen(serialize($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnData;
|
||||||
|
}
|
||||||
|
}
|
243
objects/cboden/ratchet/src/Ratchet/Session/SessionProvider.php
Normal file
243
objects/cboden/ratchet/src/Ratchet/Session/SessionProvider.php
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\Http\HttpServerInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Ratchet\Session\Storage\VirtualSessionStorage;
|
||||||
|
use Ratchet\Session\Serialize\HandlerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component will allow access to session data from your website for each user connected
|
||||||
|
* Symfony HttpFoundation is required for this component to work
|
||||||
|
* Your website must also use Symfony HttpFoundation Sessions to read your sites session data
|
||||||
|
* If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer)
|
||||||
|
*/
|
||||||
|
class SessionProvider implements HttpServerInterface {
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
protected $_app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected handler storage assigned by the developer
|
||||||
|
* @var \SessionHandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Null storage handler if no previous session was found
|
||||||
|
* @var \SessionHandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Session\Serialize\HandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\Http\HttpServerInterface $app
|
||||||
|
* @param \SessionHandlerInterface $handler
|
||||||
|
* @param array $options
|
||||||
|
* @param \Ratchet\Session\Serialize\HandlerInterface $serializer
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
public function __construct(HttpServerInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) {
|
||||||
|
$this->_app = $app;
|
||||||
|
$this->_handler = $handler;
|
||||||
|
$this->_null = new NullSessionHandler;
|
||||||
|
|
||||||
|
ini_set('session.auto_start', 0);
|
||||||
|
ini_set('session.cache_limiter', '');
|
||||||
|
ini_set('session.use_cookies', 0);
|
||||||
|
|
||||||
|
$this->setOptions($options);
|
||||||
|
|
||||||
|
if (null === $serializer) {
|
||||||
|
$serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh?
|
||||||
|
if (!class_exists($serialClass)) {
|
||||||
|
throw new \RuntimeException('Unable to parse session serialize handler');
|
||||||
|
}
|
||||||
|
|
||||||
|
$serializer = new $serialClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_serializer = $serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
$sessionName = ini_get('session.name');
|
||||||
|
|
||||||
|
$id = array_reduce($request->getHeader('Cookie'), function($accumulator, $cookie) use ($sessionName) {
|
||||||
|
if ($accumulator) {
|
||||||
|
return $accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
$crumbs = $this->parseCookie($cookie);
|
||||||
|
|
||||||
|
return isset($crumbs['cookies'][$sessionName]) ? $crumbs['cookies'][$sessionName] : false;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
if (null === $request || false === $id) {
|
||||||
|
$saveHandler = $this->_null;
|
||||||
|
$id = '';
|
||||||
|
} else {
|
||||||
|
$saveHandler = $this->_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer));
|
||||||
|
|
||||||
|
if (ini_get('session.auto_start')) {
|
||||||
|
$conn->Session->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_app->onOpen($conn, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
return $this->_app->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn) {
|
||||||
|
// "close" session for Connection
|
||||||
|
|
||||||
|
return $this->_app->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_app->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all the php session. ini options
|
||||||
|
* © Symfony
|
||||||
|
* @param array $options
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function setOptions(array $options) {
|
||||||
|
$all = array(
|
||||||
|
'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
|
||||||
|
'cookie_lifetime', 'cookie_path', 'cookie_secure',
|
||||||
|
'entropy_file', 'entropy_length', 'gc_divisor',
|
||||||
|
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
|
||||||
|
'hash_function', 'name', 'referer_check',
|
||||||
|
'serialize_handler', 'use_cookies',
|
||||||
|
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
|
||||||
|
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
|
||||||
|
'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($all as $key) {
|
||||||
|
if (!array_key_exists($key, $options)) {
|
||||||
|
$options[$key] = ini_get("session.{$key}");
|
||||||
|
} else {
|
||||||
|
ini_set("session.{$key}", $options[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $langDef Input to convert
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function toClassCase($langDef) {
|
||||||
|
return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from Guzzle3
|
||||||
|
*/
|
||||||
|
private static $cookieParts = array(
|
||||||
|
'domain' => 'Domain',
|
||||||
|
'path' => 'Path',
|
||||||
|
'max_age' => 'Max-Age',
|
||||||
|
'expires' => 'Expires',
|
||||||
|
'version' => 'Version',
|
||||||
|
'secure' => 'Secure',
|
||||||
|
'port' => 'Port',
|
||||||
|
'discard' => 'Discard',
|
||||||
|
'comment' => 'Comment',
|
||||||
|
'comment_url' => 'Comment-Url',
|
||||||
|
'http_only' => 'HttpOnly'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from Guzzle3
|
||||||
|
*/
|
||||||
|
private function parseCookie($cookie, $host = null, $path = null, $decode = false) {
|
||||||
|
// Explode the cookie string using a series of semicolons
|
||||||
|
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
|
||||||
|
|
||||||
|
// The name of the cookie (first kvp) must include an equal sign.
|
||||||
|
if (empty($pieces) || !strpos($pieces[0], '=')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the default return array
|
||||||
|
$data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
|
||||||
|
'cookies' => array(),
|
||||||
|
'data' => array(),
|
||||||
|
'path' => $path ?: '/',
|
||||||
|
'http_only' => false,
|
||||||
|
'discard' => false,
|
||||||
|
'domain' => $host
|
||||||
|
));
|
||||||
|
$foundNonCookies = 0;
|
||||||
|
|
||||||
|
// Add the cookie pieces into the parsed data array
|
||||||
|
foreach ($pieces as $part) {
|
||||||
|
|
||||||
|
$cookieParts = explode('=', $part, 2);
|
||||||
|
$key = trim($cookieParts[0]);
|
||||||
|
|
||||||
|
if (count($cookieParts) == 1) {
|
||||||
|
// Can be a single value (e.g. secure, httpOnly)
|
||||||
|
$value = true;
|
||||||
|
} else {
|
||||||
|
// Be sure to strip wrapping quotes
|
||||||
|
$value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
|
||||||
|
if ($decode) {
|
||||||
|
$value = urldecode($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check for non-cookies when cookies have been found
|
||||||
|
if (!empty($data['cookies'])) {
|
||||||
|
foreach (self::$cookieParts as $mapValue => $search) {
|
||||||
|
if (!strcasecmp($search, $key)) {
|
||||||
|
$data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
|
||||||
|
$foundNonCookies++;
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
|
||||||
|
// cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
|
||||||
|
$data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the expires date
|
||||||
|
if (!$data['expires'] && $data['max_age']) {
|
||||||
|
$data['expires'] = time() + (int) $data['max_age'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage\Proxy;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
|
|
||||||
|
class VirtualProxy extends SessionHandlerProxy {
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_sessionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __construct(\SessionHandlerInterface $handler) {
|
||||||
|
parent::__construct($handler);
|
||||||
|
|
||||||
|
$this->saveHandlerName = 'user';
|
||||||
|
$this->_sessionName = ini_get('session.name');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
return $this->_sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setId($id) {
|
||||||
|
$this->_sessionId = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getName() {
|
||||||
|
return $this->_sessionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT CALL THIS METHOD
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setName($name) {
|
||||||
|
throw new \RuntimeException("Can not change session name in VirtualProxy");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||||
|
use Ratchet\Session\Storage\Proxy\VirtualProxy;
|
||||||
|
use Ratchet\Session\Serialize\HandlerInterface;
|
||||||
|
|
||||||
|
class VirtualSessionStorage extends NativeSessionStorage {
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Session\Serialize\HandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \SessionHandlerInterface $handler
|
||||||
|
* @param string $sessionId The ID of the session to retrieve
|
||||||
|
* @param \Ratchet\Session\Serialize\HandlerInterface $serializer
|
||||||
|
*/
|
||||||
|
public function __construct(\SessionHandlerInterface $handler, $sessionId, HandlerInterface $serializer) {
|
||||||
|
$this->setSaveHandler($handler);
|
||||||
|
$this->saveHandler->setId($sessionId);
|
||||||
|
$this->_serializer = $serializer;
|
||||||
|
$this->setMetadataBag(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function start() {
|
||||||
|
if ($this->started && !$this->closed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You have to call Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::open() to use
|
||||||
|
// pdo_sqlite (and possible pdo_*) as session storage, if you are using a DSN string instead of a \PDO object
|
||||||
|
// in the constructor. The method arguments are filled with the values, which are also used by the symfony
|
||||||
|
// framework in this case. This must not be the best choice, but it works.
|
||||||
|
$this->saveHandler->open(session_save_path(), session_name());
|
||||||
|
|
||||||
|
$rawData = $this->saveHandler->read($this->saveHandler->getId());
|
||||||
|
$sessionData = $this->_serializer->unserialize($rawData);
|
||||||
|
|
||||||
|
$this->loadSession($sessionData);
|
||||||
|
|
||||||
|
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
|
||||||
|
$this->saveHandler->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function regenerate($destroy = false, $lifetime = null) {
|
||||||
|
// .. ?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save() {
|
||||||
|
// get the data from the bags?
|
||||||
|
// serialize the data
|
||||||
|
// save the data using the saveHandler
|
||||||
|
// $this->saveHandler->write($this->saveHandler->getId(),
|
||||||
|
|
||||||
|
if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
|
||||||
|
$this->saveHandler->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setSaveHandler($saveHandler = null) {
|
||||||
|
if (!($saveHandler instanceof \SessionHandlerInterface)) {
|
||||||
|
throw new \InvalidArgumentException('Handler must be instance of SessionHandlerInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($saveHandler instanceof VirtualProxy)) {
|
||||||
|
$saveHandler = new VirtualProxy($saveHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveHandler = $saveHandler;
|
||||||
|
}
|
||||||
|
}
|
5
objects/cboden/ratchet/src/Ratchet/Wamp/Exception.php
Normal file
5
objects/cboden/ratchet/src/Ratchet/Wamp/Exception.php
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
class Exception extends \Exception {
|
||||||
|
}
|
31
objects/cboden/ratchet/src/Ratchet/Wamp/JsonException.php
Normal file
31
objects/cboden/ratchet/src/Ratchet/Wamp/JsonException.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
class JsonException extends Exception {
|
||||||
|
public function __construct() {
|
||||||
|
$code = json_last_error();
|
||||||
|
|
||||||
|
switch ($code) {
|
||||||
|
case JSON_ERROR_DEPTH:
|
||||||
|
$msg = 'Maximum stack depth exceeded';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_STATE_MISMATCH:
|
||||||
|
$msg = 'Underflow or the modes mismatch';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_CTRL_CHAR:
|
||||||
|
$msg = 'Unexpected control character found';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_SYNTAX:
|
||||||
|
$msg = 'Syntax error, malformed JSON';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_UTF8:
|
||||||
|
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$msg = 'Unknown error';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($msg, $code);
|
||||||
|
}
|
||||||
|
}
|
161
objects/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php
Normal file
161
objects/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket Application Messaging Protocol
|
||||||
|
*
|
||||||
|
* @link http://wamp.ws/spec
|
||||||
|
* @link https://github.com/oberstet/autobahn-js
|
||||||
|
*
|
||||||
|
* +--------------+----+------------------+
|
||||||
|
* | Message Type | ID | DIRECTION |
|
||||||
|
* |--------------+----+------------------+
|
||||||
|
* | WELCOME | 0 | Server-to-Client |
|
||||||
|
* | PREFIX | 1 | Bi-Directional |
|
||||||
|
* | CALL | 2 | Client-to-Server |
|
||||||
|
* | CALL RESULT | 3 | Server-to-Client |
|
||||||
|
* | CALL ERROR | 4 | Server-to-Client |
|
||||||
|
* | SUBSCRIBE | 5 | Client-to-Server |
|
||||||
|
* | UNSUBSCRIBE | 6 | Client-to-Server |
|
||||||
|
* | PUBLISH | 7 | Client-to-Server |
|
||||||
|
* | EVENT | 8 | Server-to-Client |
|
||||||
|
* +--------------+----+------------------+
|
||||||
|
*/
|
||||||
|
class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
||||||
|
const MSG_WELCOME = 0;
|
||||||
|
const MSG_PREFIX = 1;
|
||||||
|
const MSG_CALL = 2;
|
||||||
|
const MSG_CALL_RESULT = 3;
|
||||||
|
const MSG_CALL_ERROR = 4;
|
||||||
|
const MSG_SUBSCRIBE = 5;
|
||||||
|
const MSG_UNSUBSCRIBE = 6;
|
||||||
|
const MSG_PUBLISH = 7;
|
||||||
|
const MSG_EVENT = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var WampServerInterface
|
||||||
|
*/
|
||||||
|
protected $_decorating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \SplObjectStorage
|
||||||
|
*/
|
||||||
|
protected $connections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WampServerInterface $serverComponent An class to propagate calls through
|
||||||
|
*/
|
||||||
|
public function __construct(WampServerInterface $serverComponent) {
|
||||||
|
$this->_decorating = $serverComponent;
|
||||||
|
$this->connections = new \SplObjectStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocols() {
|
||||||
|
if ($this->_decorating instanceof WsServerInterface) {
|
||||||
|
$subs = $this->_decorating->getSubProtocols();
|
||||||
|
$subs[] = 'wamp';
|
||||||
|
|
||||||
|
return $subs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['wamp'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$decor = new WampConnection($conn);
|
||||||
|
$this->connections->attach($conn, $decor);
|
||||||
|
|
||||||
|
$this->_decorating->onOpen($decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @throws \Ratchet\Wamp\Exception
|
||||||
|
* @throws \Ratchet\Wamp\JsonException
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$from = $this->connections[$from];
|
||||||
|
|
||||||
|
if (null === ($json = @json_decode($msg, true))) {
|
||||||
|
throw new JsonException;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($json) || $json !== array_values($json)) {
|
||||||
|
throw new Exception("Invalid WAMP message format");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))) {
|
||||||
|
throw new Exception('Invalid Topic, must be a string');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($json[0]) {
|
||||||
|
case static::MSG_PREFIX:
|
||||||
|
$from->WAMP->prefixes[$json[1]] = $json[2];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_CALL:
|
||||||
|
array_shift($json);
|
||||||
|
$callID = array_shift($json);
|
||||||
|
$procURI = array_shift($json);
|
||||||
|
|
||||||
|
if (count($json) == 1 && is_array($json[0])) {
|
||||||
|
$json = $json[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_SUBSCRIBE:
|
||||||
|
$this->_decorating->onSubscribe($from, $from->getUri($json[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_UNSUBSCRIBE:
|
||||||
|
$this->_decorating->onUnSubscribe($from, $from->getUri($json[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_PUBLISH:
|
||||||
|
$exclude = (array_key_exists(3, $json) ? $json[3] : null);
|
||||||
|
if (!is_array($exclude)) {
|
||||||
|
if (true === (boolean)$exclude) {
|
||||||
|
$exclude = [$from->WAMP->sessionId];
|
||||||
|
} else {
|
||||||
|
$exclude = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$eligible = (array_key_exists(4, $json) ? $json[4] : []);
|
||||||
|
|
||||||
|
$this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Invalid WAMP message type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$decor = $this->connections[$conn];
|
||||||
|
$this->connections->detach($conn);
|
||||||
|
|
||||||
|
$this->_decorating->onClose($decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_decorating->onError($this->connections[$conn], $e);
|
||||||
|
}
|
||||||
|
}
|
99
objects/cboden/ratchet/src/Ratchet/Wamp/Topic.php
Normal file
99
objects/cboden/ratchet/src/Ratchet/Wamp/Topic.php
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A topic/channel containing connections that have subscribed to it
|
||||||
|
*/
|
||||||
|
class Topic implements \IteratorAggregate, \Countable {
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
private $subscribers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $topicId Unique ID for this object
|
||||||
|
*/
|
||||||
|
public function __construct($topicId) {
|
||||||
|
$this->id = $topicId;
|
||||||
|
$this->subscribers = new \SplObjectStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
return $this->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to all the connections in this topic
|
||||||
|
* @param string|array $msg Payload to publish
|
||||||
|
* @param array $exclude A list of session IDs the message should be excluded from (blacklist)
|
||||||
|
* @param array $eligible A list of session Ids the message should be send to (whitelist)
|
||||||
|
* @return Topic The same Topic object to chain
|
||||||
|
*/
|
||||||
|
public function broadcast($msg, array $exclude = array(), array $eligible = array()) {
|
||||||
|
$useEligible = (bool)count($eligible);
|
||||||
|
foreach ($this->subscribers as $client) {
|
||||||
|
if (in_array($client->WAMP->sessionId, $exclude)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($useEligible && !in_array($client->WAMP->sessionId, $eligible)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$client->event($this->id, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WampConnection $conn
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function has(ConnectionInterface $conn) {
|
||||||
|
return $this->subscribers->contains($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WampConnection $conn
|
||||||
|
* @return Topic
|
||||||
|
*/
|
||||||
|
public function add(ConnectionInterface $conn) {
|
||||||
|
$this->subscribers->attach($conn);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WampConnection $conn
|
||||||
|
* @return Topic
|
||||||
|
*/
|
||||||
|
public function remove(ConnectionInterface $conn) {
|
||||||
|
if ($this->subscribers->contains($conn)) {
|
||||||
|
$this->subscribers->detach($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getIterator() {
|
||||||
|
return $this->subscribers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function count() {
|
||||||
|
return $this->subscribers->count();
|
||||||
|
}
|
||||||
|
}
|
125
objects/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php
Normal file
125
objects/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
|
||||||
|
class TopicManager implements WsServerInterface, WampServerInterface {
|
||||||
|
/**
|
||||||
|
* @var WampServerInterface
|
||||||
|
*/
|
||||||
|
protected $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $topicLookup = array();
|
||||||
|
|
||||||
|
public function __construct(WampServerInterface $app) {
|
||||||
|
$this->app = $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$conn->WAMP->subscriptions = new \SplObjectStorage;
|
||||||
|
$this->app->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
|
||||||
|
$this->app->onCall($conn, $id, $this->getTopic($topic), $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onSubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$topicObj = $this->getTopic($topic);
|
||||||
|
|
||||||
|
if ($conn->WAMP->subscriptions->contains($topicObj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->topicLookup[$topic]->add($conn);
|
||||||
|
$conn->WAMP->subscriptions->attach($topicObj);
|
||||||
|
$this->app->onSubscribe($conn, $topicObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onUnsubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$topicObj = $this->getTopic($topic);
|
||||||
|
|
||||||
|
if (!$conn->WAMP->subscriptions->contains($topicObj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cleanTopic($topicObj, $conn);
|
||||||
|
|
||||||
|
$this->app->onUnsubscribe($conn, $topicObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
|
||||||
|
$this->app->onPublish($conn, $this->getTopic($topic), $event, $exclude, $eligible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->app->onClose($conn);
|
||||||
|
|
||||||
|
foreach ($this->topicLookup as $topic) {
|
||||||
|
$this->cleanTopic($topic, $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->app->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocols() {
|
||||||
|
if ($this->app instanceof WsServerInterface) {
|
||||||
|
return $this->app->getSubProtocols();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string
|
||||||
|
* @return Topic
|
||||||
|
*/
|
||||||
|
protected function getTopic($topic) {
|
||||||
|
if (!array_key_exists($topic, $this->topicLookup)) {
|
||||||
|
$this->topicLookup[$topic] = new Topic($topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->topicLookup[$topic];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function cleanTopic(Topic $topic, ConnectionInterface $conn) {
|
||||||
|
if ($conn->WAMP->subscriptions->contains($topic)) {
|
||||||
|
$conn->WAMP->subscriptions->detach($topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->topicLookup[$topic->getId()]->remove($conn);
|
||||||
|
|
||||||
|
if (0 === $topic->count()) {
|
||||||
|
unset($this->topicLookup[$topic->getId()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
115
objects/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php
Normal file
115
objects/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
use Ratchet\Wamp\ServerProtocol as WAMP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ConnectionInterface object wrapper that is passed to your WAMP application
|
||||||
|
* representing a client. Methods on this Connection are therefore different.
|
||||||
|
* @property \stdClass $WAMP
|
||||||
|
*/
|
||||||
|
class WampConnection extends AbstractConnectionDecorator {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __construct(ConnectionInterface $conn) {
|
||||||
|
parent::__construct($conn);
|
||||||
|
|
||||||
|
$this->WAMP = new \StdClass;
|
||||||
|
$this->WAMP->sessionId = str_replace('.', '', uniqid(mt_rand(), true));
|
||||||
|
$this->WAMP->prefixes = array();
|
||||||
|
|
||||||
|
$this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successfully respond to a call made by the client
|
||||||
|
* @param string $id The unique ID given by the client to respond to
|
||||||
|
* @param array $data an object or array
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function callResult($id, $data = array()) {
|
||||||
|
return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond with an error to a client call
|
||||||
|
* @param string $id The unique ID given by the client to respond to
|
||||||
|
* @param string $errorUri The URI given to identify the specific error
|
||||||
|
* @param string $desc A developer-oriented description of the error
|
||||||
|
* @param string $details An optional human readable detail message to send back
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function callError($id, $errorUri, $desc = '', $details = null) {
|
||||||
|
if ($errorUri instanceof Topic) {
|
||||||
|
$errorUri = (string)$errorUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(WAMP::MSG_CALL_ERROR, $id, $errorUri, $desc);
|
||||||
|
|
||||||
|
if (null !== $details) {
|
||||||
|
$data[] = $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->send(json_encode($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $topic The topic to broadcast to
|
||||||
|
* @param mixed $msg Data to send with the event. Anything that is json'able
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function event($topic, $msg) {
|
||||||
|
return $this->send(json_encode(array(WAMP::MSG_EVENT, (string)$topic, $msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $curie
|
||||||
|
* @param string $uri
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function prefix($curie, $uri) {
|
||||||
|
$this->WAMP->prefixes[$curie] = (string)$uri;
|
||||||
|
|
||||||
|
return $this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, (string)$uri)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full request URI from the connection object if a prefix has been established for it
|
||||||
|
* @param string $uri
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUri($uri) {
|
||||||
|
$curieSeperator = ':';
|
||||||
|
|
||||||
|
if (preg_match('/http(s*)\:\/\//', $uri) == false) {
|
||||||
|
if (strpos($uri, $curieSeperator) !== false) {
|
||||||
|
list($prefix, $action) = explode($curieSeperator, $uri);
|
||||||
|
|
||||||
|
if(isset($this->WAMP->prefixes[$prefix]) === true){
|
||||||
|
return $this->WAMP->prefixes[$prefix] . '#' . $action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function send($data) {
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close($opt = null) {
|
||||||
|
$this->getConnection()->close($opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
objects/cboden/ratchet/src/Ratchet/Wamp/WampServer.php
Normal file
67
objects/cboden/ratchet/src/Ratchet/Wamp/WampServer.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable support for the official WAMP sub-protocol in your application
|
||||||
|
* WAMP allows for Pub/Sub and RPC
|
||||||
|
* @link http://wamp.ws The WAMP specification
|
||||||
|
* @link https://github.com/oberstet/autobahn-js Souce for client side library
|
||||||
|
* @link http://autobahn.s3.amazonaws.com/js/autobahn.min.js Minified client side library
|
||||||
|
*/
|
||||||
|
class WampServer implements MessageComponentInterface, WsServerInterface {
|
||||||
|
/**
|
||||||
|
* @var ServerProtocol
|
||||||
|
*/
|
||||||
|
protected $wampProtocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class just makes it 1 step easier to use Topic objects in WAMP
|
||||||
|
* If you're looking at the source code, look in the __construct of this
|
||||||
|
* class and use that to make your application instead of using this
|
||||||
|
*/
|
||||||
|
public function __construct(WampServerInterface $app) {
|
||||||
|
$this->wampProtocol = new ServerProtocol(new TopicManager($app));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->wampProtocol->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $conn, $msg) {
|
||||||
|
try {
|
||||||
|
$this->wampProtocol->onMessage($conn, $msg);
|
||||||
|
} catch (Exception $we) {
|
||||||
|
$conn->close(1007);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->wampProtocol->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->wampProtocol->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return $this->wampProtocol->getSubProtocols();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of Ratchet\ComponentInterface to server a WAMP application
|
||||||
|
* onMessage is replaced by various types of messages for this protocol (pub/sub or rpc)
|
||||||
|
*/
|
||||||
|
interface WampServerInterface extends ComponentInterface {
|
||||||
|
/**
|
||||||
|
* An RPC call has been received
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string $id The unique ID of the RPC, required to respond to
|
||||||
|
* @param string|Topic $topic The topic to execute the call against
|
||||||
|
* @param array $params Call parameters received from the client
|
||||||
|
*/
|
||||||
|
function onCall(ConnectionInterface $conn, $id, $topic, array $params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to subscribe to a topic has been made
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string|Topic $topic The topic to subscribe to
|
||||||
|
*/
|
||||||
|
function onSubscribe(ConnectionInterface $conn, $topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to unsubscribe from a topic has been made
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string|Topic $topic The topic to unsubscribe from
|
||||||
|
*/
|
||||||
|
function onUnSubscribe(ConnectionInterface $conn, $topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client is attempting to publish content to a subscribed connections on a URI
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string|Topic $topic The topic the user has attempted to publish to
|
||||||
|
* @param string $event Payload of the publish
|
||||||
|
* @param array $exclude A list of session IDs the message should be excluded from (blacklist)
|
||||||
|
* @param array $eligible A list of session Ids the message should be send to (whitelist)
|
||||||
|
*/
|
||||||
|
function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible);
|
||||||
|
}
|
20
objects/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php
Normal file
20
objects/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||||
|
|
||||||
|
class ConnContext {
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\WebSocket\WsConnection
|
||||||
|
*/
|
||||||
|
public $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||||
|
*/
|
||||||
|
public $buffer;
|
||||||
|
|
||||||
|
public function __construct(WsConnection $conn, MessageBuffer $buffer) {
|
||||||
|
$this->connection = $conn;
|
||||||
|
$this->buffer = $buffer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||||
|
|
||||||
|
interface MessageCallableInterface {
|
||||||
|
public function onMessage(ConnectionInterface $conn, MessageInterface $msg);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\ComponentInterface;
|
||||||
|
|
||||||
|
interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface {
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
use Ratchet\RFC6455\Messaging\DataInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\Frame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @property \StdClass $WebSocket
|
||||||
|
*/
|
||||||
|
class WsConnection extends AbstractConnectionDecorator {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function send($msg) {
|
||||||
|
if (!$this->WebSocket->closing) {
|
||||||
|
if (!($msg instanceof DataInterface)) {
|
||||||
|
$msg = new Frame($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getConnection()->send($msg->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|\Ratchet\RFC6455\Messaging\DataInterface
|
||||||
|
*/
|
||||||
|
public function close($code = 1000) {
|
||||||
|
if ($this->WebSocket->closing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code instanceof DataInterface) {
|
||||||
|
$this->send($code);
|
||||||
|
} else {
|
||||||
|
$this->send(new Frame(pack('n', $code), true, Frame::OP_CLOSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getConnection()->close();
|
||||||
|
|
||||||
|
$this->WebSocket->closing = true;
|
||||||
|
}
|
||||||
|
}
|
225
objects/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php
Normal file
225
objects/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\ComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\MessageComponentInterface as DataComponentInterface;
|
||||||
|
use Ratchet\Http\HttpServerInterface;
|
||||||
|
use Ratchet\Http\CloseResponseTrait;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\FrameInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\Frame;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||||
|
use Ratchet\RFC6455\Messaging\CloseFrameChecker;
|
||||||
|
use Ratchet\RFC6455\Handshake\ServerNegotiator;
|
||||||
|
use Ratchet\RFC6455\Handshake\RequestVerifier;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use GuzzleHttp\Psr7 as gPsr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The adapter to handle WebSocket requests/responses
|
||||||
|
* This is a mediator between the Server and your application to handle real-time messaging through a web browser
|
||||||
|
* @link http://ca.php.net/manual/en/ref.http.php
|
||||||
|
* @link http://dev.w3.org/html5/websockets/
|
||||||
|
*/
|
||||||
|
class WsServer implements HttpServerInterface {
|
||||||
|
use CloseResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorated component
|
||||||
|
* @var \Ratchet\ComponentInterface
|
||||||
|
*/
|
||||||
|
private $delegate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \SplObjectStorage
|
||||||
|
*/
|
||||||
|
protected $connections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\RFC6455\Messaging\CloseFrameChecker
|
||||||
|
*/
|
||||||
|
private $closeFrameChecker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\RFC6455\Handshake\ServerNegotiator
|
||||||
|
*/
|
||||||
|
private $handshakeNegotiator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Closure
|
||||||
|
*/
|
||||||
|
private $ueFlowFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Closure
|
||||||
|
*/
|
||||||
|
private $pongReceiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Closure
|
||||||
|
*/
|
||||||
|
private $msgCb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\WebSocket\MessageComponentInterface|\Ratchet\MessageComponentInterface $component Your application to run with WebSockets
|
||||||
|
* @note If you want to enable sub-protocols have your component implement WsServerInterface as well
|
||||||
|
*/
|
||||||
|
public function __construct(ComponentInterface $component) {
|
||||||
|
if ($component instanceof MessageComponentInterface) {
|
||||||
|
$this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) {
|
||||||
|
$this->delegate->onMessage($conn, $msg);
|
||||||
|
};
|
||||||
|
} elseif ($component instanceof DataComponentInterface) {
|
||||||
|
$this->msgCb = function(ConnectionInterface $conn, MessageInterface $msg) {
|
||||||
|
$this->delegate->onMessage($conn, $msg->getPayload());
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new \UnexpectedValueException('Expected instance of \Ratchet\WebSocket\MessageComponentInterface or \Ratchet\MessageComponentInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bin2hex('✓') !== 'e29c93') {
|
||||||
|
throw new \DomainException('Bad encoding, unicode character ✓ did not match expected value. Ensure charset UTF-8 and check ini val mbstring.func_autoload');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->delegate = $component;
|
||||||
|
$this->connections = new \SplObjectStorage;
|
||||||
|
|
||||||
|
$this->closeFrameChecker = new CloseFrameChecker;
|
||||||
|
$this->handshakeNegotiator = new ServerNegotiator(new RequestVerifier);
|
||||||
|
$this->handshakeNegotiator->setStrictSubProtocolCheck(true);
|
||||||
|
|
||||||
|
if ($component instanceof WsServerInterface) {
|
||||||
|
$this->handshakeNegotiator->setSupportedSubProtocols($component->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pongReceiver = function() {};
|
||||||
|
|
||||||
|
$reusableUnderflowException = new \UnderflowException;
|
||||||
|
$this->ueFlowFactory = function() use ($reusableUnderflowException) {
|
||||||
|
return $reusableUnderflowException;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
if (null === $request) {
|
||||||
|
throw new \UnexpectedValueException('$request can not be null');
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->httpRequest = $request;
|
||||||
|
|
||||||
|
$conn->WebSocket = new \StdClass;
|
||||||
|
$conn->WebSocket->closing = false;
|
||||||
|
|
||||||
|
$response = $this->handshakeNegotiator->handshake($request)->withHeader('X-Powered-By', \Ratchet\VERSION);
|
||||||
|
|
||||||
|
$conn->send(gPsr\str($response));
|
||||||
|
|
||||||
|
if (101 !== $response->getStatusCode()) {
|
||||||
|
return $conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
$wsConn = new WsConnection($conn);
|
||||||
|
|
||||||
|
$streamer = new MessageBuffer(
|
||||||
|
$this->closeFrameChecker,
|
||||||
|
function(MessageInterface $msg) use ($wsConn) {
|
||||||
|
$cb = $this->msgCb;
|
||||||
|
$cb($wsConn, $msg);
|
||||||
|
},
|
||||||
|
function(FrameInterface $frame) use ($wsConn) {
|
||||||
|
$this->onControlFrame($frame, $wsConn);
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
$this->ueFlowFactory
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->connections->attach($conn, new ConnContext($wsConn, $streamer));
|
||||||
|
|
||||||
|
return $this->delegate->onOpen($wsConn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
if ($from->WebSocket->closing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connections[$from]->buffer->onData($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
if ($this->connections->contains($conn)) {
|
||||||
|
$context = $this->connections[$conn];
|
||||||
|
$this->connections->detach($conn);
|
||||||
|
|
||||||
|
$this->delegate->onClose($context->connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
if ($this->connections->contains($conn)) {
|
||||||
|
$this->delegate->onError($this->connections[$conn]->connection, $e);
|
||||||
|
} else {
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onControlFrame(FrameInterface $frame, WsConnection $conn) {
|
||||||
|
switch ($frame->getOpCode()) {
|
||||||
|
case Frame::OP_CLOSE:
|
||||||
|
$conn->close($frame);
|
||||||
|
break;
|
||||||
|
case Frame::OP_PING:
|
||||||
|
$conn->send(new Frame($frame->getPayload(), true, Frame::OP_PONG));
|
||||||
|
break;
|
||||||
|
case Frame::OP_PONG:
|
||||||
|
$pongReceiver = $this->pongReceiver;
|
||||||
|
$pongReceiver($frame, $conn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStrictSubProtocolCheck($enable) {
|
||||||
|
$this->handshakeNegotiator->setStrictSubProtocolCheck($enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableKeepAlive(LoopInterface $loop, $interval = 30) {
|
||||||
|
$lastPing = new Frame(uniqid(), true, Frame::OP_PING);
|
||||||
|
$pingedConnections = new \SplObjectStorage;
|
||||||
|
$splClearer = new \SplObjectStorage;
|
||||||
|
|
||||||
|
$this->pongReceiver = function(FrameInterface $frame, $wsConn) use ($pingedConnections, &$lastPing) {
|
||||||
|
if ($frame->getPayload() === $lastPing->getPayload()) {
|
||||||
|
$pingedConnections->detach($wsConn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$loop->addPeriodicTimer((int)$interval, function() use ($pingedConnections, &$lastPing, $splClearer) {
|
||||||
|
foreach ($pingedConnections as $wsConn) {
|
||||||
|
$wsConn->close();
|
||||||
|
}
|
||||||
|
$pingedConnections->removeAllExcept($splClearer);
|
||||||
|
|
||||||
|
$lastPing = new Frame(uniqid(), true, Frame::OP_PING);
|
||||||
|
|
||||||
|
foreach ($this->connections as $key => $conn) {
|
||||||
|
$wsConn = $this->connections[$conn]->connection;
|
||||||
|
|
||||||
|
$wsConn->send($lastPing);
|
||||||
|
$pingedConnections->attach($wsConn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket Server Interface
|
||||||
|
*/
|
||||||
|
interface WsServerInterface {
|
||||||
|
/**
|
||||||
|
* If any component in a stack supports a WebSocket sub-protocol return each supported in an array
|
||||||
|
* @return array
|
||||||
|
* @todo This method may be removed in future version (note that will not break code, just make some code obsolete)
|
||||||
|
*/
|
||||||
|
function getSubProtocols();
|
||||||
|
}
|
36
objects/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php
Normal file
36
objects/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
class BinaryEcho implements \Ratchet\WebSocket\MessageComponentInterface {
|
||||||
|
public function onMessage(ConnectionInterface $from, \Ratchet\RFC6455\Messaging\MessageInterface $msg) {
|
||||||
|
$from->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$port = $argc > 1 ? $argv[1] : 8000;
|
||||||
|
$impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect');
|
||||||
|
|
||||||
|
$loop = new $impl;
|
||||||
|
$sock = new React\Socket\Server('0.0.0.0:' . $port, $loop);
|
||||||
|
|
||||||
|
$wsServer = new Ratchet\WebSocket\WsServer(new BinaryEcho);
|
||||||
|
// This is enabled to test https://github.com/ratchetphp/Ratchet/issues/430
|
||||||
|
// The time is left at 10 minutes so that it will not try to every ping anything
|
||||||
|
// This causes the Ratchet server to crash on test 2.7
|
||||||
|
$wsServer->enableKeepAlive($loop, 600);
|
||||||
|
|
||||||
|
$app = new Ratchet\Http\HttpServer($wsServer);
|
||||||
|
|
||||||
|
$server = new Ratchet\Server\IoServer($app, $sock, $loop);
|
||||||
|
$server->run();
|
15
objects/cboden/ratchet/tests/autobahn/fuzzingclient-all.json
Normal file
15
objects/cboden/ratchet/tests/autobahn/fuzzingclient-all.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"options": {"failByDrop": false}
|
||||||
|
, "outdir": "reports/ab"
|
||||||
|
|
||||||
|
, "servers": [
|
||||||
|
{"agent": "Ratchet/0.4 libevent", "url": "ws://localhost:8001", "options": {"version": 18}}
|
||||||
|
, {"agent": "Ratchet/0.4 libev", "url": "ws://localhost:8004", "options": {"version": 18}}
|
||||||
|
, {"agent": "Ratchet/0.4 streams", "url": "ws://localhost:8002", "options": {"version": 18}}
|
||||||
|
, {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||||
|
]
|
||||||
|
|
||||||
|
, "cases": ["*"]
|
||||||
|
, "exclude-cases": []
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"options": {"failByDrop": false}
|
||||||
|
, "outdir": "reports/profile"
|
||||||
|
|
||||||
|
, "servers": [
|
||||||
|
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||||
|
]
|
||||||
|
|
||||||
|
, "cases": ["9.7.4"]
|
||||||
|
, "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"]
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"options": {"failByDrop": false}
|
||||||
|
, "outdir": "reports/rfc"
|
||||||
|
|
||||||
|
, "servers": [
|
||||||
|
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||||
|
]
|
||||||
|
|
||||||
|
, "cases": ["*"]
|
||||||
|
, "exclude-cases": []
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
|
}
|
4
objects/cboden/ratchet/tests/bootstrap.php
Normal file
4
objects/cboden/ratchet/tests/bootstrap.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$loader = require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
$loader->addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet');
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_app;
|
||||||
|
protected $_serv;
|
||||||
|
protected $_conn;
|
||||||
|
|
||||||
|
abstract public function getConnectionClassString();
|
||||||
|
abstract public function getDecoratorClassString();
|
||||||
|
abstract public function getComponentClassString();
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_app = $this->getMock($this->getComponentClassString());
|
||||||
|
$decorator = $this->getDecoratorClassString();
|
||||||
|
$this->_serv = new $decorator($this->_app);
|
||||||
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
|
||||||
|
$this->doOpen($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doOpen($conn) {
|
||||||
|
$this->_serv->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isExpectedConnection() {
|
||||||
|
return new \PHPUnit_Framework_Constraint_IsInstanceOf($this->getConnectionClassString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOpen() {
|
||||||
|
$this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
|
||||||
|
$this->doOpen($this->getMock('\Ratchet\ConnectionInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnClose() {
|
||||||
|
$this->_app->expects($this->once())->method('onClose')->with($this->isExpectedConnection());
|
||||||
|
$this->_serv->onClose($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnError() {
|
||||||
|
$e = new \Exception('Whoops!');
|
||||||
|
$this->_app->expects($this->once())->method('onError')->with($this->isExpectedConnection(), $e);
|
||||||
|
$this->_serv->onError($this->_conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passthroughMessageTest($value) {
|
||||||
|
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $value);
|
||||||
|
$this->_serv->onMessage($this->_conn, $value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class Component implements MessageComponentInterface, WsServerInterface {
|
||||||
|
public $last = array();
|
||||||
|
|
||||||
|
public $protocols = array();
|
||||||
|
|
||||||
|
public function __construct(ComponentInterface $app = null) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return $this->protocols;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class Connection implements ConnectionInterface {
|
||||||
|
public $last = array(
|
||||||
|
'send' => ''
|
||||||
|
, 'close' => false
|
||||||
|
);
|
||||||
|
|
||||||
|
public $remoteAddress = '127.0.0.1';
|
||||||
|
|
||||||
|
public function send($data) {
|
||||||
|
$this->last[__FUNCTION__] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->last[__FUNCTION__] = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
|
||||||
|
class ConnectionDecorator extends AbstractConnectionDecorator {
|
||||||
|
public $last = array(
|
||||||
|
'write' => ''
|
||||||
|
, 'end' => false
|
||||||
|
);
|
||||||
|
|
||||||
|
public function send($data) {
|
||||||
|
$this->last[__FUNCTION__] = $data;
|
||||||
|
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->last[__FUNCTION__] = true;
|
||||||
|
|
||||||
|
$this->getConnection()->close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class WampComponent implements WampServerInterface, WsServerInterface {
|
||||||
|
public $last = array();
|
||||||
|
|
||||||
|
public $protocols = array();
|
||||||
|
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return $this->protocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUnSubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
|
||||||
|
class NullComponent implements MessageComponentInterface, WsServerInterface, WampServerInterface {
|
||||||
|
public function onOpen(ConnectionInterface $conn) {}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $conn, $msg) {}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {}
|
||||||
|
|
||||||
|
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {}
|
||||||
|
|
||||||
|
public function onSubscribe(ConnectionInterface $conn, $topic) {}
|
||||||
|
|
||||||
|
public function onUnSubscribe(ConnectionInterface $conn, $topic) {}
|
||||||
|
|
||||||
|
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude = array(), array $eligible = array()) {}
|
||||||
|
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp\Stub;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
|
||||||
|
interface WsWampServerInterface extends WsServerInterface, WampServerInterface {
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket\Stub;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
|
||||||
|
interface WsMessageComponentInterface extends MessageComponentInterface, WsServerInterface {
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
use Ratchet\Mock\ConnectionDecorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\AbstractConnectionDecorator
|
||||||
|
* @covers Ratchet\ConnectionInterface
|
||||||
|
*/
|
||||||
|
class AbstractConnectionDecoratorTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $mock;
|
||||||
|
protected $l1;
|
||||||
|
protected $l2;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->l1 = new ConnectionDecorator($this->mock);
|
||||||
|
$this->l2 = new ConnectionDecorator($this->l1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGet() {
|
||||||
|
$var = 'hello';
|
||||||
|
$val = 'world';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->l1->$var);
|
||||||
|
$this->assertEquals($val, $this->l2->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSet() {
|
||||||
|
$var = 'Chris';
|
||||||
|
$val = 'Boden';
|
||||||
|
|
||||||
|
$this->l1->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->mock->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetLevel2() {
|
||||||
|
$var = 'Try';
|
||||||
|
$val = 'Again';
|
||||||
|
|
||||||
|
$this->l2->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->mock->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsSetTrue() {
|
||||||
|
$var = 'PHP';
|
||||||
|
$val = 'Ratchet';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
|
||||||
|
$this->assertTrue(isset($this->l1->$var));
|
||||||
|
$this->assertTrue(isset($this->l2->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsSetFalse() {
|
||||||
|
$var = 'herp';
|
||||||
|
$val = 'derp';
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->l1->$var));
|
||||||
|
$this->assertFalse(isset($this->l2->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnset() {
|
||||||
|
$var = 'Flying';
|
||||||
|
$val = 'Monkey';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
unset($this->l1->$var);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mock->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsetLevel2() {
|
||||||
|
$var = 'Flying';
|
||||||
|
$val = 'Monkey';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
unset($this->l2->$var);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mock->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetConnection() {
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$conn = $method->invokeArgs($this->l1, array());
|
||||||
|
|
||||||
|
$this->assertSame($this->mock, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetConnectionLevel2() {
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$conn = $method->invokeArgs($this->l2, array());
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWrapperCanStoreSelfInDecorator() {
|
||||||
|
$this->mock->decorator = $this->l1;
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $this->l2->decorator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorRecursion() {
|
||||||
|
$this->mock->decorator = new \stdClass;
|
||||||
|
$this->mock->decorator->conn = $this->l1;
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $this->mock->decorator->conn);
|
||||||
|
$this->assertSame($this->l1, $this->l1->decorator->conn);
|
||||||
|
$this->assertSame($this->l1, $this->l2->decorator->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorRecursionLevel2() {
|
||||||
|
$this->mock->decorator = new \stdClass;
|
||||||
|
$this->mock->decorator->conn = $this->l2;
|
||||||
|
|
||||||
|
$this->assertSame($this->l2, $this->mock->decorator->conn);
|
||||||
|
$this->assertSame($this->l2, $this->l1->decorator->conn);
|
||||||
|
$this->assertSame($this->l2, $this->l2->decorator->conn);
|
||||||
|
|
||||||
|
// just for fun
|
||||||
|
$this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothing() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->mock->nonExistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothingLevel1() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->l1->nonExistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothingLevel2() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->l2->nonExistant;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\HttpRequestParser
|
||||||
|
*/
|
||||||
|
class HttpRequestParserTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $parser;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->parser = new HttpRequestParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headersProvider() {
|
||||||
|
return array(
|
||||||
|
array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n1")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖\r\n\r\n")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie\r\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider headersProvider
|
||||||
|
*/
|
||||||
|
public function testIsEom($expected, $message) {
|
||||||
|
$this->assertEquals($expected, $this->parser->isEom($message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBufferOverflowResponse() {
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
|
||||||
|
$this->parser->maxSize = 20;
|
||||||
|
|
||||||
|
$this->assertNull($this->parser->onMessage($conn, "GET / HTTP/1.1\r\n"));
|
||||||
|
|
||||||
|
$this->setExpectedException('OverflowException');
|
||||||
|
|
||||||
|
$this->parser->onMessage($conn, "Header-Is: Too Big");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnTypeIsRequest() {
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n");
|
||||||
|
|
||||||
|
$this->assertInstanceOf('\Psr\Http\Message\RequestInterface', $return);
|
||||||
|
}
|
||||||
|
}
|
64
objects/cboden/ratchet/tests/unit/Http/HttpServerTest.php
Normal file
64
objects/cboden/ratchet/tests/unit/Http/HttpServerTest.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\HttpServer
|
||||||
|
*/
|
||||||
|
class HttpServerTest extends AbstractMessageComponentTestCase {
|
||||||
|
public function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->_conn->httpHeadersReceived = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\ConnectionInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServer';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOpen() {
|
||||||
|
$headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
|
||||||
|
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
|
||||||
|
$this->_serv->onMessage($this->_conn, $headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageAfterHeaders() {
|
||||||
|
$headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_serv->onMessage($this->_conn, $headers);
|
||||||
|
|
||||||
|
$message = "Hello World!";
|
||||||
|
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
|
||||||
|
$this->_serv->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBufferOverflow() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
|
||||||
|
$this->_serv->onMessage($this->_conn, str_repeat('a', 5000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseIfNotEstablished() {
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_serv->onError($this->_conn, new \Exception('Whoops!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBufferHeaders() {
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_app->expects($this->never())->method('onOpen');
|
||||||
|
$this->_app->expects($this->never())->method('onMessage');
|
||||||
|
|
||||||
|
$this->_serv->onMessage($this->_conn, "GET / HTTP/1.1");
|
||||||
|
}
|
||||||
|
}
|
46
objects/cboden/ratchet/tests/unit/Http/OriginCheckTest.php
Normal file
46
objects/cboden/ratchet/tests/unit/Http/OriginCheckTest.php
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\OriginCheck
|
||||||
|
*/
|
||||||
|
class OriginCheckTest extends AbstractMessageComponentTestCase {
|
||||||
|
protected $_reqStub;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_reqStub = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||||
|
$this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue(['localhost']));
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->_serv->allowedOrigins[] = 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doOpen($conn) {
|
||||||
|
$this->_serv->onOpen($conn, $this->_reqStub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\ConnectionInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return '\Ratchet\Http\OriginCheck';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseOnNonMatchingOrigin() {
|
||||||
|
$this->_serv->allowedOrigins = ['socketo.me'];
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$this->_serv->onOpen($this->_conn, $this->_reqStub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessage() {
|
||||||
|
$this->passthroughMessageTest('Hello World!');
|
||||||
|
}
|
||||||
|
}
|
165
objects/cboden/ratchet/tests/unit/Http/RouterTest.php
Normal file
165
objects/cboden/ratchet/tests/unit/Http/RouterTest.php
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||||
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\Router
|
||||||
|
*/
|
||||||
|
class RouterTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_router;
|
||||||
|
protected $_matcher;
|
||||||
|
protected $_conn;
|
||||||
|
protected $_uri;
|
||||||
|
protected $_req;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_uri = $this->getMock('Psr\Http\Message\UriInterface');
|
||||||
|
$this->_req = $this->getMock('\Psr\Http\Message\RequestInterface');
|
||||||
|
$this->_req
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getUri')
|
||||||
|
->will($this->returnValue($this->_uri));
|
||||||
|
$this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
|
||||||
|
$this->_matcher
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getContext')
|
||||||
|
->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')));
|
||||||
|
$this->_router = new Router($this->_matcher);
|
||||||
|
|
||||||
|
$this->_uri->expects($this->any())->method('getPath')->will($this->returnValue('ws://doesnt.matter/'));
|
||||||
|
$this->_uri->expects($this->any())->method('withQuery')->with($this->callback(function($val) {
|
||||||
|
$this->setResult($val);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}))->will($this->returnSelf());
|
||||||
|
$this->_uri->expects($this->any())->method('getQuery')->will($this->returnCallback([$this, 'getResult']));
|
||||||
|
$this->_req->expects($this->any())->method('withUri')->will($this->returnSelf());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFourOhFour() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$nope = new ResourceNotFoundException;
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->throwException($nope));
|
||||||
|
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNullRequest() {
|
||||||
|
$this->setExpectedException('\UnexpectedValueException');
|
||||||
|
$this->_router->onOpen($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerIsMessageComponentInterface() {
|
||||||
|
$this->setExpectedException('\UnexpectedValueException');
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => new \StdClass)));
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnOpen() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
|
||||||
|
$expectedConn = new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\ConnectionInterface');
|
||||||
|
$controller->expects($this->once())->method('onOpen')->with($expectedConn, $this->_req);
|
||||||
|
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnMessageBubbles() {
|
||||||
|
$message = "The greatest trick the Devil ever pulled was convincing the world he didn't exist";
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$controller->expects($this->once())->method('onMessage')->with($this->_conn, $message);
|
||||||
|
|
||||||
|
$this->_conn->controller = $controller;
|
||||||
|
|
||||||
|
$this->_router->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnCloseBubbles() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$controller->expects($this->once())->method('onClose')->with($this->_conn);
|
||||||
|
|
||||||
|
$this->_conn->controller = $controller;
|
||||||
|
|
||||||
|
$this->_router->onClose($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnErrorBubbles() {
|
||||||
|
$e= new \Exception('One cannot be betrayed if one has no exceptions');
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$controller->expects($this->once())->method('onError')->with($this->_conn, $e);
|
||||||
|
|
||||||
|
$this->_conn->controller = $controller;
|
||||||
|
|
||||||
|
$this->_router->onError($this->_conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRouterGeneratesRouteParameters() {
|
||||||
|
/** @var $controller WsServerInterface */
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
/** @var $matcher UrlMatcherInterface */
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will(
|
||||||
|
$this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
|
||||||
|
);
|
||||||
|
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||||
|
|
||||||
|
$router = new Router($this->_matcher);
|
||||||
|
|
||||||
|
$router->onOpen($conn, $this->_req);
|
||||||
|
|
||||||
|
$this->assertEquals('foo=bar&baz=qux', $this->_req->getUri()->getQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQueryParams() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will(
|
||||||
|
$this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||||
|
$request = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||||
|
$uri = new \GuzzleHttp\Psr7\Uri('ws://doesnt.matter/endpoint?hello=world&foo=nope');
|
||||||
|
|
||||||
|
$request->expects($this->any())->method('getUri')->will($this->returnCallback(function() use (&$uri) {
|
||||||
|
return $uri;
|
||||||
|
}));
|
||||||
|
$request->expects($this->any())->method('withUri')->with($this->callback(function($url) use (&$uri) {
|
||||||
|
$uri = $url;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}))->will($this->returnSelf());
|
||||||
|
|
||||||
|
$router = new Router($this->_matcher);
|
||||||
|
$router->onOpen($conn, $request);
|
||||||
|
|
||||||
|
$this->assertEquals('foo=nope&baz=qux&hello=world', $request->getUri()->getQuery());
|
||||||
|
$this->assertEquals('ws', $request->getUri()->getScheme());
|
||||||
|
$this->assertEquals('doesnt.matter', $request->getUri()->getHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testImpatientClientOverflow() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$header = "GET /nope HTTP/1.1
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: upgrade
|
||||||
|
Host: localhost
|
||||||
|
Origin: http://localhost
|
||||||
|
Sec-WebSocket-Version: 13\r\n\r\n";
|
||||||
|
|
||||||
|
$app = new HttpServer(new Router(new UrlMatcher(new RouteCollection, new RequestContext)));
|
||||||
|
$app->onOpen($this->_conn);
|
||||||
|
$app->onMessage($this->_conn, $header);
|
||||||
|
$app->onMessage($this->_conn, 'Silly body');
|
||||||
|
}
|
||||||
|
}
|
26
objects/cboden/ratchet/tests/unit/Server/EchoServerTest.php
Normal file
26
objects/cboden/ratchet/tests/unit/Server/EchoServerTest.php
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\Server\EchoServer;
|
||||||
|
|
||||||
|
class EchoServerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_conn;
|
||||||
|
protected $_comp;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_comp = new EchoServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageEchod() {
|
||||||
|
$message = 'Tillsonburg, my back still aches when I hear that word.';
|
||||||
|
$this->_conn->expects($this->once())->method('send')->with($message);
|
||||||
|
$this->_comp->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testErrorClosesConnection() {
|
||||||
|
ob_start();
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_comp->onError($this->_conn, new \Exception);
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Application\Server;
|
||||||
|
use Ratchet\Server\FlashPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\FlashPolicy
|
||||||
|
*/
|
||||||
|
class FlashPolicyTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
protected $_policy;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_policy = new FlashPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPolicyRender() {
|
||||||
|
$this->_policy->setSiteControl('all');
|
||||||
|
$this->_policy->addAllowedAccess('example.com', '*');
|
||||||
|
$this->_policy->addAllowedAccess('dev.example.com', '*');
|
||||||
|
|
||||||
|
$this->assertInstanceOf('SimpleXMLElement', $this->_policy->renderPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidPolicyReader() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
$this->_policy->renderPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidDomainPolicyReader() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
$this->_policy->setSiteControl('all');
|
||||||
|
$this->_policy->addAllowedAccess('dev.example.*', '*');
|
||||||
|
$this->_policy->renderPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider siteControl
|
||||||
|
*/
|
||||||
|
public function testSiteControlValidation($accept, $permittedCrossDomainPolicies) {
|
||||||
|
$this->assertEquals($accept, $this->_policy->validateSiteControl($permittedCrossDomainPolicies));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function siteControl() {
|
||||||
|
return array(
|
||||||
|
array(true, 'all')
|
||||||
|
, array(true, 'none')
|
||||||
|
, array(true, 'master-only')
|
||||||
|
, array(false, 'by-content-type')
|
||||||
|
, array(false, 'by-ftp-filename')
|
||||||
|
, array(false, '')
|
||||||
|
, array(false, 'all ')
|
||||||
|
, array(false, 'asdf')
|
||||||
|
, array(false, '@893830')
|
||||||
|
, array(false, '*')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider URI
|
||||||
|
*/
|
||||||
|
public function testDomainValidation($accept, $domain) {
|
||||||
|
$this->assertEquals($accept, $this->_policy->validateDomain($domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function URI() {
|
||||||
|
return array(
|
||||||
|
array(true, '*')
|
||||||
|
, array(true, 'example.com')
|
||||||
|
, array(true, 'exam-ple.com')
|
||||||
|
, array(true, '*.example.com')
|
||||||
|
, array(true, 'www.example.com')
|
||||||
|
, array(true, 'dev.dev.example.com')
|
||||||
|
, array(true, 'http://example.com')
|
||||||
|
, array(true, 'https://example.com')
|
||||||
|
, array(true, 'http://*.example.com')
|
||||||
|
, array(false, 'exam*ple.com')
|
||||||
|
, array(true, '127.0.255.1')
|
||||||
|
, array(true, 'localhost')
|
||||||
|
, array(false, 'www.example.*')
|
||||||
|
, array(false, 'www.exa*le.com')
|
||||||
|
, array(false, 'www.example.*com')
|
||||||
|
, array(false, '*.example.*')
|
||||||
|
, array(false, 'gasldf*$#a0sdf0a8sdf')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider ports
|
||||||
|
*/
|
||||||
|
public function testPortValidation($accept, $ports) {
|
||||||
|
$this->assertEquals($accept, $this->_policy->validatePorts($ports));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ports() {
|
||||||
|
return array(
|
||||||
|
array(true, '*')
|
||||||
|
, array(true, '80')
|
||||||
|
, array(true, '80,443')
|
||||||
|
, array(true, '507,516-523')
|
||||||
|
, array(true, '507,516-523,333')
|
||||||
|
, array(true, '507,516-523,507,516-523')
|
||||||
|
, array(false, '516-')
|
||||||
|
, array(true, '516-523,11')
|
||||||
|
, array(false, '516,-523,11')
|
||||||
|
, array(false, 'example')
|
||||||
|
, array(false, 'asdf,123')
|
||||||
|
, array(false, '--')
|
||||||
|
, array(false, ',,,')
|
||||||
|
, array(false, '838*')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAllowedAccessOnlyAcceptsValidPorts() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
|
||||||
|
$this->_policy->addAllowedAccess('*', 'nope');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetSiteControlThrowsException() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
|
||||||
|
$this->_policy->setSiteControl('nope');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testErrorClosesConnection() {
|
||||||
|
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$this->_policy->onError($conn, new \Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageSendsString() {
|
||||||
|
$this->_policy->addAllowedAccess('*', '*');
|
||||||
|
|
||||||
|
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn->expects($this->once())->method('send')->with($this->isType('string'));
|
||||||
|
|
||||||
|
$this->_policy->onMessage($conn, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpenExists() {
|
||||||
|
$this->assertTrue(method_exists($this->_policy, 'onOpen'));
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_policy->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCloseExists() {
|
||||||
|
$this->assertTrue(method_exists($this->_policy, 'onClose'));
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_policy->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Application\Server;
|
||||||
|
use Ratchet\Server\IoConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\IoConnection
|
||||||
|
*/
|
||||||
|
class IoConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $sock;
|
||||||
|
protected $conn;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->sock = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||||
|
$this->conn = new IoConnection($this->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseBubbles() {
|
||||||
|
$this->sock->expects($this->once())->method('end');
|
||||||
|
$this->conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendBubbles() {
|
||||||
|
$msg = '6 hour rides are productive';
|
||||||
|
|
||||||
|
$this->sock->expects($this->once())->method('write')->with($msg);
|
||||||
|
$this->conn->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendReturnsSelf() {
|
||||||
|
$this->assertSame($this->conn, $this->conn->send('fluent interface'));
|
||||||
|
}
|
||||||
|
}
|
127
objects/cboden/ratchet/tests/unit/Server/IoServerTest.php
Normal file
127
objects/cboden/ratchet/tests/unit/Server/IoServerTest.php
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\Server\IoServer;
|
||||||
|
use React\EventLoop\StreamSelectLoop;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Socket\Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\IoServer
|
||||||
|
*/
|
||||||
|
class IoServerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
protected $app;
|
||||||
|
|
||||||
|
protected $port;
|
||||||
|
|
||||||
|
protected $reactor;
|
||||||
|
|
||||||
|
protected function tickLoop(LoopInterface $loop) {
|
||||||
|
$loop->futureTick(function () use ($loop) {
|
||||||
|
$loop->stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
$loop->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->app = $this->getMock('\\Ratchet\\MessageComponentInterface');
|
||||||
|
|
||||||
|
$loop = new StreamSelectLoop;
|
||||||
|
$this->reactor = new Server(0, $loop);
|
||||||
|
|
||||||
|
$uri = $this->reactor->getAddress();
|
||||||
|
$this->port = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_PORT);
|
||||||
|
$this->server = new IoServer($this->app, $this->reactor, $loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpen() {
|
||||||
|
$this->app->expects($this->once())->method('onOpen')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
|
||||||
|
|
||||||
|
$client = stream_socket_client("tcp://localhost:{$this->port}");
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
//$this->assertTrue(is_string($this->app->last['onOpen'][0]->remoteAddress));
|
||||||
|
//$this->assertTrue(is_int($this->app->last['onOpen'][0]->resourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnData() {
|
||||||
|
$msg = 'Hello World!';
|
||||||
|
|
||||||
|
$this->app->expects($this->once())->method('onMessage')->with(
|
||||||
|
$this->isInstanceOf('\\Ratchet\\ConnectionInterface')
|
||||||
|
, $msg
|
||||||
|
);
|
||||||
|
|
||||||
|
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
|
||||||
|
socket_set_block($client);
|
||||||
|
socket_connect($client, 'localhost', $this->port);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
socket_write($client, $msg);
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
socket_shutdown($client, 1);
|
||||||
|
socket_shutdown($client, 0);
|
||||||
|
socket_close($client);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnClose() {
|
||||||
|
$this->app->expects($this->once())->method('onClose')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
|
||||||
|
|
||||||
|
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
|
||||||
|
socket_set_block($client);
|
||||||
|
socket_connect($client, 'localhost', $this->port);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
socket_shutdown($client, 1);
|
||||||
|
socket_shutdown($client, 0);
|
||||||
|
socket_close($client);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFactory() {
|
||||||
|
$this->assertInstanceOf('\\Ratchet\\Server\\IoServer', IoServer::factory($this->app, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoLoopProvidedError() {
|
||||||
|
$this->setExpectedException('RuntimeException');
|
||||||
|
|
||||||
|
$io = new IoServer($this->app, $this->reactor);
|
||||||
|
$io->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnErrorPassesException() {
|
||||||
|
$conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||||
|
$conn->decor = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$err = new \Exception("Nope");
|
||||||
|
|
||||||
|
$this->app->expects($this->once())->method('onError')->with($conn->decor, $err);
|
||||||
|
|
||||||
|
$this->server->handleError($err, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onErrorCalledWhenExceptionThrown() {
|
||||||
|
$this->markTestIncomplete("Need to learn how to throw an exception from a mock");
|
||||||
|
|
||||||
|
$conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||||
|
$this->server->handleConnect($conn);
|
||||||
|
|
||||||
|
$e = new \Exception;
|
||||||
|
$this->app->expects($this->once())->method('onMessage')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'), 'f')->will($e);
|
||||||
|
$this->app->expects($this->once())->method('onError')->with($this->instanceOf('\\Ratchet\\ConnectionInterface', $e));
|
||||||
|
|
||||||
|
$this->server->handleData('f', $conn);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\Server\IpBlackList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\IpBlackList
|
||||||
|
*/
|
||||||
|
class IpBlackListTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $blocker;
|
||||||
|
protected $mock;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = $this->getMock('\\Ratchet\\MessageComponentInterface');
|
||||||
|
$this->blocker = new IpBlackList($this->mock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpen() {
|
||||||
|
$this->mock->expects($this->exactly(3))->method('onOpen');
|
||||||
|
|
||||||
|
$conn1 = $this->newConn();
|
||||||
|
$conn2 = $this->newConn();
|
||||||
|
$conn3 = $this->newConn();
|
||||||
|
|
||||||
|
$this->blocker->onOpen($conn1);
|
||||||
|
$this->blocker->onOpen($conn3);
|
||||||
|
$this->blocker->onOpen($conn2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockDoesNotTriggerOnOpen() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->blocker->blockAddress($conn->remoteAddress);
|
||||||
|
|
||||||
|
$this->mock->expects($this->never())->method('onOpen');
|
||||||
|
|
||||||
|
$ret = $this->blocker->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockDoesNotTriggerOnClose() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->blocker->blockAddress($conn->remoteAddress);
|
||||||
|
|
||||||
|
$this->mock->expects($this->never())->method('onClose');
|
||||||
|
|
||||||
|
$ret = $this->blocker->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageDecoration() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$msg = 'Hello not being blocked';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onMessage')->with($conn, $msg);
|
||||||
|
|
||||||
|
$this->blocker->onMessage($conn, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCloseDecoration() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onClose')->with($conn);
|
||||||
|
|
||||||
|
$this->blocker->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockClosesConnection() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$this->blocker->blockAddress($conn->remoteAddress);
|
||||||
|
|
||||||
|
$conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$this->blocker->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAndRemoveWithFluentInterfaces() {
|
||||||
|
$blockOne = '127.0.0.1';
|
||||||
|
$blockTwo = '192.168.1.1';
|
||||||
|
$unblock = '75.119.207.140';
|
||||||
|
|
||||||
|
$this->blocker
|
||||||
|
->blockAddress($unblock)
|
||||||
|
->blockAddress($blockOne)
|
||||||
|
->unblockAddress($unblock)
|
||||||
|
->blockAddress($blockTwo)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->assertEquals(array($blockOne, $blockTwo), $this->blocker->getBlockedAddresses());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorPassesErrors() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$e = new \Exception('I threw an error');
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onError')->with($conn, $e);
|
||||||
|
|
||||||
|
$this->blocker->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addressProvider() {
|
||||||
|
return array(
|
||||||
|
array('127.0.0.1', '127.0.0.1')
|
||||||
|
, array('localhost', 'localhost')
|
||||||
|
, array('fe80::1%lo0', 'fe80::1%lo0')
|
||||||
|
, array('127.0.0.1', '127.0.0.1:6392')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider addressProvider
|
||||||
|
*/
|
||||||
|
public function testFilterAddress($expected, $input) {
|
||||||
|
$this->assertEquals($expected, $this->blocker->filterAddress($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnblockingSilentlyFails() {
|
||||||
|
$this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->blocker->unblockAddress('localhost'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn->remoteAddress = '127.0.0.1';
|
||||||
|
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
use Ratchet\Session\Serialize\PhpHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Session\Serialize\PhpHandler
|
||||||
|
*/
|
||||||
|
class PhpHandlerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_handler;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_handler = new PhpHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serializedProvider() {
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'
|
||||||
|
, array(
|
||||||
|
'_sf2_attributes' => array(
|
||||||
|
'hello' => 'world'
|
||||||
|
, 'last' => 1332872102
|
||||||
|
)
|
||||||
|
, '_sf2_flashes' => array()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider serializedProvider
|
||||||
|
*/
|
||||||
|
public function testUnserialize($in, $expected) {
|
||||||
|
$this->assertEquals($expected, $this->_handler->unserialize($in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider serializedProvider
|
||||||
|
*/
|
||||||
|
public function testSerialize($serialized, $original) {
|
||||||
|
$this->assertEquals($serialized, $this->_handler->serialize($original));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Session\SessionProvider
|
||||||
|
* @covers Ratchet\Session\Storage\VirtualSessionStorage
|
||||||
|
* @covers Ratchet\Session\Storage\Proxy\VirtualProxy
|
||||||
|
*/
|
||||||
|
class SessionProviderTest extends AbstractMessageComponentTestCase {
|
||||||
|
public function setUp() {
|
||||||
|
if (!class_exists('Symfony\Component\HttpFoundation\Session\Session')) {
|
||||||
|
return $this->markTestSkipped('Dependency of Symfony HttpFoundation failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
$this->_serv = new SessionProvider($this->_app, new NullSessionHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
ini_set('session.serialize_handler', 'php');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\ConnectionInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return '\Ratchet\NullComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function classCaseProvider() {
|
||||||
|
return array(
|
||||||
|
array('php', 'Php')
|
||||||
|
, array('php_binary', 'PhpBinary')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider classCaseProvider
|
||||||
|
*/
|
||||||
|
public function testToClassCase($in, $out) {
|
||||||
|
$ref = new \ReflectionClass('\\Ratchet\\Session\\SessionProvider');
|
||||||
|
$method = $ref->getMethod('toClassCase');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$component = new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface'));
|
||||||
|
$this->assertEquals($out, $method->invokeArgs($component, array($in)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I think I have severely butchered this test...it's not so much of a unit test as it is a full-fledged component test
|
||||||
|
*/
|
||||||
|
public function testConnectionValueFromPdo() {
|
||||||
|
if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
|
||||||
|
return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionId = md5('testSession');
|
||||||
|
|
||||||
|
$dbOptions = array(
|
||||||
|
'db_table' => 'sessions'
|
||||||
|
, 'db_id_col' => 'sess_id'
|
||||||
|
, 'db_data_col' => 'sess_data'
|
||||||
|
, 'db_time_col' => 'sess_time'
|
||||||
|
, 'db_lifetime_col' => 'sess_lifetime'
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdo = new \PDO("sqlite::memory:");
|
||||||
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->exec(vsprintf("CREATE TABLE %s (%s TEXT NOT NULL PRIMARY KEY, %s BLOB NOT NULL, %s INTEGER NOT NULL, %s INTEGER)", $dbOptions));
|
||||||
|
|
||||||
|
$pdoHandler = new PdoSessionHandler($pdo, $dbOptions);
|
||||||
|
$pdoHandler->write($sessionId, '_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}');
|
||||||
|
|
||||||
|
$component = new SessionProvider($this->getMock($this->getComponentClassString()), $pdoHandler, array('auto_start' => 1));
|
||||||
|
$connection = $this->getMock('Ratchet\\ConnectionInterface');
|
||||||
|
|
||||||
|
$headers = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||||
|
$headers->expects($this->once())->method('getHeader')->will($this->returnValue([ini_get('session.name') . "={$sessionId};"]));
|
||||||
|
|
||||||
|
$component->onOpen($connection, $headers);
|
||||||
|
|
||||||
|
$this->assertEquals('world', $connection->Session->get('hello'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
$conn = $this->getMock('Ratchet\ConnectionInterface');
|
||||||
|
|
||||||
|
$headers = $this->getMock('Psr\Http\Message\Request', array('getCookie'), array('POST', '/', array()));
|
||||||
|
$headers->expects($this->once())->method('getCookie', array(ini_get('session.name')))->will($this->returnValue(null));
|
||||||
|
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageDecorator() {
|
||||||
|
$message = "Database calls are usually blocking :(";
|
||||||
|
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
|
||||||
|
$this->_serv->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRejectInvalidSeralizers() {
|
||||||
|
if (!function_exists('wddx_serialize_value')) {
|
||||||
|
$this->markTestSkipped();
|
||||||
|
}
|
||||||
|
|
||||||
|
ini_set('session.serialize_handler', 'wddx');
|
||||||
|
$this->setExpectedException('\RuntimeException');
|
||||||
|
new SessionProvider($this->getMock($this->getComponentClassString()), $this->getMock('\SessionHandlerInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doOpen($conn) {
|
||||||
|
$request = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||||
|
$request->expects($this->any())->method('getHeader')->will($this->returnValue([]));
|
||||||
|
|
||||||
|
$this->_serv->onOpen($conn, $request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage;
|
||||||
|
use Ratchet\Session\Serialize\PhpHandler;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||||
|
|
||||||
|
class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
/**
|
||||||
|
* @var VirtualSessionStorage
|
||||||
|
*/
|
||||||
|
protected $_virtualSessionStorage;
|
||||||
|
|
||||||
|
protected $_pathToDB;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
|
||||||
|
return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = <<<SQL
|
||||||
|
CREATE TABLE `sessions` (
|
||||||
|
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
|
||||||
|
`sess_data` BLOB NOT NULL,
|
||||||
|
`sess_time` INTEGER UNSIGNED NOT NULL,
|
||||||
|
`sess_lifetime` MEDIUMINT NOT NULL
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
$this->_pathToDB = tempnam(sys_get_temp_dir(), 'SQ3');;
|
||||||
|
$dsn = 'sqlite:' . $this->_pathToDB;
|
||||||
|
|
||||||
|
$pdo = new \PDO($dsn);
|
||||||
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->exec($schema);
|
||||||
|
$pdo = null;
|
||||||
|
|
||||||
|
$sessionHandler = new PdoSessionHandler($dsn);
|
||||||
|
$serializer = new PhpHandler();
|
||||||
|
$this->_virtualSessionStorage = new VirtualSessionStorage($sessionHandler, 'foobar', $serializer);
|
||||||
|
$this->_virtualSessionStorage->registerBag(new FlashBag());
|
||||||
|
$this->_virtualSessionStorage->registerBag(new AttributeBag());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
unlink($this->_pathToDB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStartWithDSN() {
|
||||||
|
$this->_virtualSessionStorage->start();
|
||||||
|
|
||||||
|
$this->assertTrue($this->_virtualSessionStorage->isStarted());
|
||||||
|
}
|
||||||
|
}
|
295
objects/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php
Normal file
295
objects/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\Mock\Connection;
|
||||||
|
use Ratchet\Mock\WampComponent as TestComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Ratchet\Wamp\ServerProtocol
|
||||||
|
* @covers \Ratchet\Wamp\WampServerInterface
|
||||||
|
* @covers \Ratchet\Wamp\WampConnection
|
||||||
|
*/
|
||||||
|
class ServerProtocolTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_comp;
|
||||||
|
|
||||||
|
protected $_app;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_app = new TestComponent;
|
||||||
|
$this->_comp = new ServerProtocol($this->_app);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
return new Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invalidMessageProvider() {
|
||||||
|
return [
|
||||||
|
[0]
|
||||||
|
, [3]
|
||||||
|
, [4]
|
||||||
|
, [8]
|
||||||
|
, [9]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider invalidMessageProvider
|
||||||
|
*/
|
||||||
|
public function testInvalidMessages($type) {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode([$type]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWelcomeMessage() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$message = $conn->last['send'];
|
||||||
|
$json = json_decode($message);
|
||||||
|
|
||||||
|
$this->assertEquals(4, count($json));
|
||||||
|
$this->assertEquals(0, $json[0]);
|
||||||
|
$this->assertTrue(is_string($json[1]));
|
||||||
|
$this->assertEquals(1, $json[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubscribe() {
|
||||||
|
$uri = 'http://example.com';
|
||||||
|
$clientMessage = array(5, $uri);
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $this->_app->last['onSubscribe'][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnSubscribe() {
|
||||||
|
$uri = 'http://example.com/endpoint';
|
||||||
|
$clientMessage = array(6, $uri);
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callProvider() {
|
||||||
|
return [
|
||||||
|
[2, 'a', 'b']
|
||||||
|
, [2, ['a', 'b']]
|
||||||
|
, [1, 'one']
|
||||||
|
, [3, 'one', 'two', 'three']
|
||||||
|
, [3, ['un', 'deux', 'trois']]
|
||||||
|
, [2, 'hi', ['hello', 'world']]
|
||||||
|
, [2, ['hello', 'world'], 'hi']
|
||||||
|
, [2, ['hello' => 'world', 'herp' => 'derp']]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider callProvider
|
||||||
|
*/
|
||||||
|
public function testCall() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$paramNum = array_shift($args);
|
||||||
|
|
||||||
|
$uri = 'http://example.com/endpoint/' . rand(1, 100);
|
||||||
|
$id = uniqid('', false);
|
||||||
|
$clientMessage = array_merge(array(2, $id, $uri), $args);
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($id, $this->_app->last['onCall'][1]);
|
||||||
|
$this->assertEquals($uri, $this->_app->last['onCall'][2]);
|
||||||
|
|
||||||
|
$this->assertEquals($paramNum, count($this->_app->last['onCall'][3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublish() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$topic = 'pubsubhubbub';
|
||||||
|
$event = 'Here I am, publishing data';
|
||||||
|
|
||||||
|
$clientMessage = array(7, $topic, $event);
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($topic, $this->_app->last['onPublish'][1]);
|
||||||
|
$this->assertEquals($event, $this->_app->last['onPublish'][2]);
|
||||||
|
$this->assertEquals(array(), $this->_app->last['onPublish'][3]);
|
||||||
|
$this->assertEquals(array(), $this->_app->last['onPublish'][4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublishAndExcludeMe() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', true)));
|
||||||
|
|
||||||
|
$this->assertEquals($conn->WAMP->sessionId, $this->_app->last['onPublish'][3][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublishAndEligible() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$buddy = uniqid('', false);
|
||||||
|
$friend = uniqid('', false);
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend))));
|
||||||
|
|
||||||
|
$this->assertEquals(array(), $this->_app->last['onPublish'][3]);
|
||||||
|
$this->assertEquals(2, count($this->_app->last['onPublish'][4]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eventProvider() {
|
||||||
|
return array(
|
||||||
|
array('http://example.com', array('one', 'two'))
|
||||||
|
, array('curie', array(array('hello' => 'world', 'herp' => 'derp')))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider eventProvider
|
||||||
|
*/
|
||||||
|
public function testEvent($topic, $payload) {
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$conn->event($topic, $payload);
|
||||||
|
|
||||||
|
$eventString = $conn->last['send'];
|
||||||
|
|
||||||
|
$this->assertSame(array(8, $topic, $payload), json_decode($eventString, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnClosePropagation() {
|
||||||
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onClose($conn);
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$check = $method->invokeArgs($this->_app->last['onClose'][0], array());
|
||||||
|
|
||||||
|
$this->assertSame($conn, $check);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnErrorPropagation() {
|
||||||
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$e = new \Exception('Nope');
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onError($conn, $e);
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$check = $method->invokeArgs($this->_app->last['onError'][0], array());
|
||||||
|
|
||||||
|
$this->assertSame($conn, $check);
|
||||||
|
$this->assertSame($e, $this->_app->last['onError'][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrefix() {
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$prefix = 'incoming';
|
||||||
|
$fullURI = "http://example.com/$prefix";
|
||||||
|
$method = 'call';
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array(1, $prefix, $fullURI)));
|
||||||
|
|
||||||
|
$this->assertEquals($fullURI, $conn->WAMP->prefixes[$prefix]);
|
||||||
|
$this->assertEquals("$fullURI#$method", $conn->getUri("$prefix:$method"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageMustBeJson() {
|
||||||
|
$this->setExpectedException('\\Ratchet\\Wamp\\JsonException');
|
||||||
|
|
||||||
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, 'Hello World!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsReturnsArray() {
|
||||||
|
$this->assertTrue(is_array($this->_comp->getSubProtocols()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsGetFromApp() {
|
||||||
|
$this->_app->protocols = array('hello', 'world');
|
||||||
|
|
||||||
|
$this->assertGreaterThanOrEqual(3, count($this->_comp->getSubProtocols()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWampOnMessageApp() {
|
||||||
|
$app = $this->getMock('\\Ratchet\\Wamp\\WampServerInterface');
|
||||||
|
$wamp = new ServerProtocol($app);
|
||||||
|
|
||||||
|
$this->assertContains('wamp', $wamp->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function badFormatProvider() {
|
||||||
|
return array(
|
||||||
|
array(json_encode(true))
|
||||||
|
, array('{"valid":"json", "invalid": "message"}')
|
||||||
|
, array('{"0": "fail", "hello": "world"}')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider badFormatProvider
|
||||||
|
*/
|
||||||
|
public function testValidJsonButInvalidProtocol($message) {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadClientInputFromNonStringTopic() {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode([5, ['hells', 'nope']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadPrefixWithNonStringTopic() {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode([1, ['hells', 'nope'], ['bad', 'input']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadPublishWithNonStringTopic() {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode([7, ['bad', 'input'], 'Hider']));
|
||||||
|
}
|
||||||
|
}
|
226
objects/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php
Normal file
226
objects/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\TopicManager
|
||||||
|
*/
|
||||||
|
class TopicManagerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
private $mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Wamp\TopicManager
|
||||||
|
*/
|
||||||
|
private $mngr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\ConnectionInterface
|
||||||
|
*/
|
||||||
|
private $conn;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface');
|
||||||
|
$this->mngr = new TopicManager($this->mock);
|
||||||
|
|
||||||
|
$this->conn->WAMP = new \StdClass;
|
||||||
|
$this->mngr->onOpen($this->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTopicReturnsTopicObject() {
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array('The Topic'));
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Ratchet\Wamp\Topic', $topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTopicCreatesTopicWithSameName() {
|
||||||
|
$name = 'The Topic';
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
$this->assertEquals($name, $topic->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTopicReturnsSameObject() {
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array('No copy'));
|
||||||
|
$again = $method->invokeArgs($this->mngr, array('No copy'));
|
||||||
|
|
||||||
|
$this->assertSame($topic, $again);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpen() {
|
||||||
|
$this->mock->expects($this->once())->method('onOpen');
|
||||||
|
$this->mngr->onOpen($this->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCall() {
|
||||||
|
$id = uniqid();
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onCall')->with(
|
||||||
|
$this->conn
|
||||||
|
, $id
|
||||||
|
, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
, array()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onCall($this->conn, $id, 'new topic', array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnSubscribeCreatesTopicObject() {
|
||||||
|
$this->mock->expects($this->once())->method('onSubscribe')->with(
|
||||||
|
$this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, 'new topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTopicIsInConnectionOnSubscribe() {
|
||||||
|
$name = 'New Topic';
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
|
||||||
|
$this->assertTrue($this->conn->WAMP->subscriptions->contains($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoubleSubscriptionFiresOnce() {
|
||||||
|
$this->mock->expects($this->exactly(1))->method('onSubscribe');
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, 'same topic');
|
||||||
|
$this->mngr->onSubscribe($this->conn, 'same topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeEvent() {
|
||||||
|
$name = 'in and out';
|
||||||
|
$this->mock->expects($this->once())->method('onUnsubscribe')->with(
|
||||||
|
$this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeFiresOnce() {
|
||||||
|
$name = 'getting sleepy';
|
||||||
|
$this->mock->expects($this->exactly(1))->method('onUnsubscribe');
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeRemovesTopicFromConnection() {
|
||||||
|
$name = 'Bye Bye Topic';
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
|
||||||
|
$this->assertFalse($this->conn->WAMP->subscriptions->contains($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnPublishBubbles() {
|
||||||
|
$msg = 'Cover all the code!';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onPublish')->with(
|
||||||
|
$this->conn
|
||||||
|
, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
, $msg
|
||||||
|
, $this->isType('array')
|
||||||
|
, $this->isType('array')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onPublish($this->conn, 'topic coverage', $msg, array(), array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCloseBubbles() {
|
||||||
|
$this->mock->expects($this->once())->method('onClose')->with($this->conn);
|
||||||
|
$this->mngr->onClose($this->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function topicProvider($name) {
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$attribute = $class->getProperty('topicLookup');
|
||||||
|
$attribute->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
return array($topic, $attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnIsRemovedFromTopicOnClose() {
|
||||||
|
$name = 'State Testing';
|
||||||
|
list($topic, $attribute) = $this->topicProvider($name);
|
||||||
|
|
||||||
|
$this->assertCount(1, $attribute->getValue($this->mngr));
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onClose($this->conn);
|
||||||
|
|
||||||
|
$this->assertFalse($topic->has($this->conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function topicConnExpectationProvider() {
|
||||||
|
return [
|
||||||
|
[ 'onClose', 0]
|
||||||
|
, ['onUnsubscribe', 0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider topicConnExpectationProvider
|
||||||
|
*/
|
||||||
|
public function testTopicRetentionFromLeavingConnections($methodCall, $expectation) {
|
||||||
|
$topicName = 'checkTopic';
|
||||||
|
list($topic, $attribute) = $this->topicProvider($topicName);
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $topicName);
|
||||||
|
call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName));
|
||||||
|
|
||||||
|
$this->assertCount($expectation, $attribute->getValue($this->mngr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnErrorBubbles() {
|
||||||
|
$e = new \Exception('All work and no play makes Chris a dull boy');
|
||||||
|
$this->mock->expects($this->once())->method('onError')->with($this->conn, $e);
|
||||||
|
|
||||||
|
$this->mngr->onError($this->conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsReturnsArray() {
|
||||||
|
$this->assertInternalType('array', $this->mngr->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsBubbles() {
|
||||||
|
$subs = array('hello', 'world');
|
||||||
|
$app = $this->getMock('Ratchet\Wamp\Stub\WsWampServerInterface');
|
||||||
|
$app->expects($this->once())->method('getSubProtocols')->will($this->returnValue($subs));
|
||||||
|
$mngr = new TopicManager($app);
|
||||||
|
|
||||||
|
$this->assertEquals($subs, $mngr->getSubProtocols());
|
||||||
|
}
|
||||||
|
}
|
164
objects/cboden/ratchet/tests/unit/Wamp/TopicTest.php
Normal file
164
objects/cboden/ratchet/tests/unit/Wamp/TopicTest.php
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\Topic
|
||||||
|
*/
|
||||||
|
class TopicTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
public function testGetId() {
|
||||||
|
$id = uniqid();
|
||||||
|
$topic = new Topic($id);
|
||||||
|
|
||||||
|
$this->assertEquals($id, $topic->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAndCount() {
|
||||||
|
$topic = new Topic('merp');
|
||||||
|
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
|
||||||
|
$this->assertEquals(3, count($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemove() {
|
||||||
|
$topic = new Topic('boop');
|
||||||
|
$tracked = $this->newConn();
|
||||||
|
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
$topic->add($tracked);
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
|
||||||
|
$topic->remove($tracked);
|
||||||
|
|
||||||
|
$this->assertEquals(2, count($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBroadcast() {
|
||||||
|
$msg = 'Hello World!';
|
||||||
|
$name = 'Batman';
|
||||||
|
$protocol = json_encode(array(8, $name, $msg));
|
||||||
|
|
||||||
|
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
|
||||||
|
$first->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$second->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$topic = new Topic($name);
|
||||||
|
$topic->add($first);
|
||||||
|
$topic->add($second);
|
||||||
|
|
||||||
|
$topic->broadcast($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBroadcastWithExclude() {
|
||||||
|
$msg = 'Hello odd numbers';
|
||||||
|
$name = 'Excluding';
|
||||||
|
$protocol = json_encode(array(8, $name, $msg));
|
||||||
|
|
||||||
|
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
|
||||||
|
$first->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$second->expects($this->never())->method('send');
|
||||||
|
|
||||||
|
$third->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$topic = new Topic($name);
|
||||||
|
$topic->add($first);
|
||||||
|
$topic->add($second);
|
||||||
|
$topic->add($third);
|
||||||
|
|
||||||
|
$topic->broadcast($msg, array($second->WAMP->sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBroadcastWithEligible() {
|
||||||
|
$msg = 'Hello white list';
|
||||||
|
$name = 'Eligible';
|
||||||
|
$protocol = json_encode(array(8, $name, $msg));
|
||||||
|
|
||||||
|
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
|
||||||
|
$first->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$second->expects($this->never())->method('send');
|
||||||
|
|
||||||
|
$third->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$topic = new Topic($name);
|
||||||
|
$topic->add($first);
|
||||||
|
$topic->add($second);
|
||||||
|
$topic->add($third);
|
||||||
|
|
||||||
|
$topic->broadcast($msg, array(), array($first->WAMP->sessionId, $third->WAMP->sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIterator() {
|
||||||
|
$first = $this->newConn();
|
||||||
|
$second = $this->newConn();
|
||||||
|
$third = $this->newConn();
|
||||||
|
|
||||||
|
$topic = new Topic('Joker');
|
||||||
|
$topic->add($first)->add($second)->add($third);
|
||||||
|
|
||||||
|
$check = array($first, $second, $third);
|
||||||
|
|
||||||
|
foreach ($topic as $mock) {
|
||||||
|
$this->assertNotSame(false, array_search($mock, $check));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testToString() {
|
||||||
|
$name = 'Bane';
|
||||||
|
$topic = new Topic($name);
|
||||||
|
|
||||||
|
$this->assertEquals($name, (string)$topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesHave() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$topic = new Topic('Two Face');
|
||||||
|
$topic->add($conn);
|
||||||
|
|
||||||
|
$this->assertTrue($topic->has($conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesNotHave() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$topic = new Topic('Alfred');
|
||||||
|
|
||||||
|
$this->assertFalse($topic->has($conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesNotHaveAfterRemove() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$topic = new Topic('Ras');
|
||||||
|
|
||||||
|
$topic->add($conn)->remove($conn);
|
||||||
|
|
||||||
|
$this->assertFalse($topic->has($conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
return new WampConnection($this->getMock('\\Ratchet\\ConnectionInterface'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\WampConnection
|
||||||
|
*/
|
||||||
|
class WampConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $conn;
|
||||||
|
protected $mock;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$this->conn = new WampConnection($this->mock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallResult() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$data = array('hello' => 'world', 'herp' => 'derp');
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(3, $callId, $data)));
|
||||||
|
|
||||||
|
$this->conn->callResult($callId, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallError() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
|
||||||
|
|
||||||
|
$this->conn->callError($callId, $uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallErrorWithTopic() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
|
||||||
|
|
||||||
|
$this->conn->callError($callId, new Topic($uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetailedCallError() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
$desc = 'beep boop beep';
|
||||||
|
$detail = 'Error: Too much awesome';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, $desc, $detail)));
|
||||||
|
|
||||||
|
$this->conn->callError($callId, $uri, $desc, $detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrefix() {
|
||||||
|
$shortOut = 'outgoing';
|
||||||
|
$longOut = 'http://example.com/outgoing';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(1, $shortOut, $longOut)));
|
||||||
|
|
||||||
|
$this->conn->prefix($shortOut, $longOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetUriWhenNoCurieGiven() {
|
||||||
|
$uri = 'http://example.com/noshort';
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $this->conn->getUri($uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClose() {
|
||||||
|
$mock = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn = new WampConnection($mock);
|
||||||
|
|
||||||
|
$mock->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
49
objects/cboden/ratchet/tests/unit/Wamp/WampServerTest.php
Normal file
49
objects/cboden/ratchet/tests/unit/Wamp/WampServerTest.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\WampServer
|
||||||
|
*/
|
||||||
|
class WampServerTest extends AbstractMessageComponentTestCase {
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\Wamp\WampConnection';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return 'Ratchet\Wamp\WampServer';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Wamp\WampServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageToEvent() {
|
||||||
|
$published = 'Client published this message';
|
||||||
|
|
||||||
|
$this->_app->expects($this->once())->method('onPublish')->with(
|
||||||
|
$this->isExpectedConnection()
|
||||||
|
, new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\Wamp\Topic')
|
||||||
|
, $published
|
||||||
|
, array()
|
||||||
|
, array()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->_serv->onMessage($this->_conn, json_encode(array(7, 'topic', $published)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocols() {
|
||||||
|
// todo: could expand on this
|
||||||
|
$this->assertInternalType('array', $this->_serv->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnectionClosesOnInvalidJson() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_serv->onMessage($this->_conn, 'invalid json');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnectionClosesOnProtocolError() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_serv->onMessage($this->_conn, json_encode(array('valid' => 'json', 'invalid' => 'protocol')));
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,8 +37,8 @@ namespace Composer\Autoload;
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
* @see https://www.php-fig.org/psr/psr-0/
|
* @see http://www.php-fig.org/psr/psr-0/
|
||||||
* @see https://www.php-fig.org/psr/psr-4/
|
* @see http://www.php-fig.org/psr/psr-4/
|
||||||
*/
|
*/
|
||||||
class ClassLoader
|
class ClassLoader
|
||||||
{
|
{
|
||||||
|
@ -60,7 +60,7 @@ class ClassLoader
|
||||||
public function getPrefixes()
|
public function getPrefixes()
|
||||||
{
|
{
|
||||||
if (!empty($this->prefixesPsr0)) {
|
if (!empty($this->prefixesPsr0)) {
|
||||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array();
|
return array();
|
||||||
|
|
|
@ -11,7 +11,6 @@ return array(
|
||||||
'AVCSequenceParameterSetReader' => $vendorDir . '/james-heinrich/getid3/getid3/module.audio-video.flv.php',
|
'AVCSequenceParameterSetReader' => $vendorDir . '/james-heinrich/getid3/getid3/module.audio-video.flv.php',
|
||||||
'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
||||||
'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
||||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
|
||||||
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||||
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||||
'Google_AccessToken_Revoke' => $vendorDir . '/google/apiclient/src/aliases.php',
|
'Google_AccessToken_Revoke' => $vendorDir . '/google/apiclient/src/aliases.php',
|
||||||
|
|
|
@ -6,16 +6,20 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
|
||||||
|
'972fda704d680a3a53c68e34e193cb22' => $vendorDir . '/react/promise-timer/src/functions_include.php',
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
|
||||||
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
||||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
|
||||||
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
||||||
|
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||||
|
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||||
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
|
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
|
||||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||||
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
|
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
|
||||||
'a8d3953fd9959404dd22d3dfcd0a79f0' => $vendorDir . '/google/apiclient/src/aliases.php',
|
'a8d3953fd9959404dd22d3dfcd0a79f0' => $vendorDir . '/google/apiclient/src/aliases.php',
|
||||||
|
'3be16222a6efa6dd226a219eaaff823b' => $vendorDir . '/ratchet/pawl/src/functions_include.php',
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,4 +8,5 @@ $baseDir = dirname($vendorDir);
|
||||||
return array(
|
return array(
|
||||||
'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
|
'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
|
||||||
'Google_Service_' => array($vendorDir . '/google/apiclient-services/src'),
|
'Google_Service_' => array($vendorDir . '/google/apiclient-services/src'),
|
||||||
|
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,8 +9,21 @@ return array(
|
||||||
'phpseclib\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
|
'phpseclib\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
|
||||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
||||||
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
|
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
|
||||||
|
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||||
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
||||||
|
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
|
||||||
|
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
|
||||||
|
'React\\Stream\\' => array($vendorDir . '/react/stream/src'),
|
||||||
|
'React\\Socket\\' => array($vendorDir . '/react/socket/src'),
|
||||||
|
'React\\Promise\\Timer\\' => array($vendorDir . '/react/promise-timer/src'),
|
||||||
|
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
|
||||||
|
'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'),
|
||||||
|
'React\\Dns\\' => array($vendorDir . '/react/dns/src'),
|
||||||
|
'React\\Cache\\' => array($vendorDir . '/react/cache/src'),
|
||||||
|
'Ratchet\\RFC6455\\' => array($vendorDir . '/ratchet/rfc6455/src'),
|
||||||
|
'Ratchet\\Client\\' => array($vendorDir . '/ratchet/pawl/src'),
|
||||||
|
'Ratchet\\' => array($vendorDir . '/cboden/ratchet/src/Ratchet'),
|
||||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||||
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
|
||||||
|
|
|
@ -13,24 +13,19 @@ class ComposerAutoloaderInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \Composer\Autoload\ClassLoader
|
|
||||||
*/
|
|
||||||
public static function getLoader()
|
public static function getLoader()
|
||||||
{
|
{
|
||||||
if (null !== self::$loader) {
|
if (null !== self::$loader) {
|
||||||
return self::$loader;
|
return self::$loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
require __DIR__ . '/platform_check.php';
|
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit78837c7a9c090d873268c14c1a5daf3d', 'loadClassLoader'), true, true);
|
spl_autoload_register(array('ComposerAutoloaderInit78837c7a9c090d873268c14c1a5daf3d', 'loadClassLoader'), true, true);
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit78837c7a9c090d873268c14c1a5daf3d', 'loadClassLoader'));
|
spl_autoload_unregister(array('ComposerAutoloaderInit78837c7a9c090d873268c14c1a5daf3d', 'loadClassLoader'));
|
||||||
|
|
||||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||||
if ($useStaticLoader) {
|
if ($useStaticLoader) {
|
||||||
require __DIR__ . '/autoload_static.php';
|
require_once __DIR__ . '/autoload_static.php';
|
||||||
|
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d::getInitializer($loader));
|
call_user_func(\Composer\Autoload\ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d::getInitializer($loader));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,18 +7,22 @@ namespace Composer\Autoload;
|
||||||
class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
{
|
{
|
||||||
public static $files = array (
|
public static $files = array (
|
||||||
|
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||||
|
'972fda704d680a3a53c68e34e193cb22' => __DIR__ . '/..' . '/react/promise-timer/src/functions_include.php',
|
||||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||||
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
|
||||||
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
|
||||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
|
||||||
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
||||||
|
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||||
|
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||||
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
|
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
|
||||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||||
|
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||||
'2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
|
'2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
|
||||||
'a8d3953fd9959404dd22d3dfcd0a79f0' => __DIR__ . '/..' . '/google/apiclient/src/aliases.php',
|
'a8d3953fd9959404dd22d3dfcd0a79f0' => __DIR__ . '/..' . '/google/apiclient/src/aliases.php',
|
||||||
|
'3be16222a6efa6dd226a219eaaff823b' => __DIR__ . '/..' . '/ratchet/pawl/src/functions_include.php',
|
||||||
);
|
);
|
||||||
|
|
||||||
public static $prefixLengthsPsr4 = array (
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
@ -30,8 +34,24 @@ class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
array (
|
array (
|
||||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
'Symfony\\Polyfill\\Php72\\' => 23,
|
||||||
'Symfony\\Polyfill\\Php70\\' => 23,
|
'Symfony\\Polyfill\\Php70\\' => 23,
|
||||||
|
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||||
|
'Symfony\\Component\\Routing\\' => 26,
|
||||||
|
'Symfony\\Component\\HttpFoundation\\' => 33,
|
||||||
|
),
|
||||||
|
'R' =>
|
||||||
|
array (
|
||||||
|
'React\\Stream\\' => 13,
|
||||||
|
'React\\Socket\\' => 13,
|
||||||
|
'React\\Promise\\Timer\\' => 20,
|
||||||
|
'React\\Promise\\' => 14,
|
||||||
|
'React\\EventLoop\\' => 16,
|
||||||
|
'React\\Dns\\' => 10,
|
||||||
|
'React\\Cache\\' => 12,
|
||||||
|
'Ratchet\\RFC6455\\' => 16,
|
||||||
|
'Ratchet\\Client\\' => 15,
|
||||||
|
'Ratchet\\' => 8,
|
||||||
),
|
),
|
||||||
'P' =>
|
'P' =>
|
||||||
array (
|
array (
|
||||||
|
@ -75,6 +95,10 @@ class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
|
0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
|
||||||
),
|
),
|
||||||
|
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||||
|
),
|
||||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
|
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
|
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
|
||||||
|
@ -83,6 +107,54 @@ class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
|
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn',
|
||||||
),
|
),
|
||||||
|
'Symfony\\Component\\Routing\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/routing',
|
||||||
|
),
|
||||||
|
'Symfony\\Component\\HttpFoundation\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/symfony/http-foundation',
|
||||||
|
),
|
||||||
|
'React\\Stream\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/stream/src',
|
||||||
|
),
|
||||||
|
'React\\Socket\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/socket/src',
|
||||||
|
),
|
||||||
|
'React\\Promise\\Timer\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/promise-timer/src',
|
||||||
|
),
|
||||||
|
'React\\Promise\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/promise/src',
|
||||||
|
),
|
||||||
|
'React\\EventLoop\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/event-loop/src',
|
||||||
|
),
|
||||||
|
'React\\Dns\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/dns/src',
|
||||||
|
),
|
||||||
|
'React\\Cache\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/react/cache/src',
|
||||||
|
),
|
||||||
|
'Ratchet\\RFC6455\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/ratchet/rfc6455/src',
|
||||||
|
),
|
||||||
|
'Ratchet\\Client\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/ratchet/pawl/src',
|
||||||
|
),
|
||||||
|
'Ratchet\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/cboden/ratchet/src/Ratchet',
|
||||||
|
),
|
||||||
'Psr\\Log\\' =>
|
'Psr\\Log\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||||
|
@ -148,6 +220,13 @@ class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
0 => __DIR__ . '/..' . '/google/apiclient-services/src',
|
0 => __DIR__ . '/..' . '/google/apiclient-services/src',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
'E' =>
|
||||||
|
array (
|
||||||
|
'Evenement' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/evenement/evenement/src',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
public static $classMap = array (
|
public static $classMap = array (
|
||||||
|
@ -156,7 +235,6 @@ class ComposerStaticInit78837c7a9c090d873268c14c1a5daf3d
|
||||||
'AVCSequenceParameterSetReader' => __DIR__ . '/..' . '/james-heinrich/getid3/getid3/module.audio-video.flv.php',
|
'AVCSequenceParameterSetReader' => __DIR__ . '/..' . '/james-heinrich/getid3/getid3/module.audio-video.flv.php',
|
||||||
'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
||||||
'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
||||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
|
||||||
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||||
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||||
'Google_AccessToken_Revoke' => __DIR__ . '/..' . '/google/apiclient/src/aliases.php',
|
'Google_AccessToken_Revoke' => __DIR__ . '/..' . '/google/apiclient/src/aliases.php',
|
||||||
|
|
File diff suppressed because it is too large
Load diff
2
objects/evenement/evenement/.gitignore
vendored
Normal file
2
objects/evenement/evenement/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
composer.lock
|
||||||
|
vendor
|
26
objects/evenement/evenement/.travis.yml
Normal file
26
objects/evenement/evenement/.travis.yml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
language: php
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.4
|
||||||
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
- 7.0
|
||||||
|
- 7.1
|
||||||
|
- hhvm
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- php: hhvm
|
||||||
|
- php: nightly
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- wget http://getcomposer.org/composer.phar
|
||||||
|
- php composer.phar install
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./vendor/bin/phpunit --coverage-text
|
||||||
|
- php -n examples/benchmark-emit-no-arguments.php
|
||||||
|
- php -n examples/benchmark-emit-one-argument.php
|
||||||
|
- php -n examples/benchmark-emit.php
|
||||||
|
- php -n examples/benchmark-emit-once.php
|
8
objects/evenement/evenement/CHANGELOG.md
Normal file
8
objects/evenement/evenement/CHANGELOG.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
* 2.0.0 (2012-11-02)
|
||||||
|
|
||||||
|
* Require PHP >=5.4.0
|
||||||
|
* Added EventEmitterTrait
|
||||||
|
* Removed EventEmitter2
|
19
objects/evenement/evenement/LICENSE
Normal file
19
objects/evenement/evenement/LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2011 Igor Wiedler
|
||||||
|
|
||||||
|
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.
|
83
objects/evenement/evenement/README.md
Normal file
83
objects/evenement/evenement/README.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# Événement
|
||||||
|
|
||||||
|
Événement is a very simple event dispatching library for PHP.
|
||||||
|
|
||||||
|
It has the same design goals as [Silex](http://silex-project.org) and
|
||||||
|
[Pimple](http://pimple-project.org), to empower the user while staying concise
|
||||||
|
and simple.
|
||||||
|
|
||||||
|
It is very strongly inspired by the EventEmitter API found in
|
||||||
|
[node.js](http://nodejs.org).
|
||||||
|
|
||||||
|
[](http://travis-ci.org/igorw/evenement)
|
||||||
|
|
||||||
|
## Fetch
|
||||||
|
|
||||||
|
The recommended way to install Événement is [through composer](http://getcomposer.org).
|
||||||
|
|
||||||
|
Just create a composer.json file for your project:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"evenement/evenement": "2.0.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** The `2.0.*` version of Événement requires PHP 5.4. If you are
|
||||||
|
using PHP 5.3, please use the `1.0.*` version:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"evenement/evenement": "1.0.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And run these two commands to install it:
|
||||||
|
|
||||||
|
$ curl -s http://getcomposer.org/installer | php
|
||||||
|
$ php composer.phar install
|
||||||
|
|
||||||
|
Now you can add the autoloader, and you will have access to the library:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Creating an Emitter
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
$emitter = new Evenement\EventEmitter();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Listeners
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
$emitter->on('user.created', function (User $user) use ($logger) {
|
||||||
|
$logger->log(sprintf("User '%s' was created.", $user->getLogin()));
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emitting Events
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
$emitter->emit('user.created', array($user));
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests
|
||||||
|
-----
|
||||||
|
|
||||||
|
$ phpunit
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
MIT, see LICENSE.
|
34
objects/evenement/evenement/composer.json
Normal file
34
objects/evenement/evenement/composer.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "evenement/evenement",
|
||||||
|
"description": "Événement is a very simple event dispatching library for PHP",
|
||||||
|
"keywords": ["event-dispatcher", "event-emitter"],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Igor Wiedler",
|
||||||
|
"email": "igor@wiedler.ch"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^6.0||^5.7||^4.8.35"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Evenement": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-0": {
|
||||||
|
"Evenement": "tests"
|
||||||
|
},
|
||||||
|
"files": ["tests/Evenement/Tests/functions.php"]
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.0-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
objects/evenement/evenement/doc/00-intro.md
Normal file
28
objects/evenement/evenement/doc/00-intro.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Événement is is French and means "event". The événement library aims to
|
||||||
|
provide a simple way of subscribing to events and notifying those subscribers
|
||||||
|
whenever an event occurs.
|
||||||
|
|
||||||
|
The API that it exposes is almost a direct port of the EventEmitter API found
|
||||||
|
in node.js. It also includes an "EventEmitter". There are some minor
|
||||||
|
differences however.
|
||||||
|
|
||||||
|
The EventEmitter is an implementation of the publish-subscribe pattern, which
|
||||||
|
is a generalized version of the observer pattern. The observer pattern
|
||||||
|
specifies an observable subject, which observers can register themselves to.
|
||||||
|
Once something interesting happens, the subject notifies its observers.
|
||||||
|
|
||||||
|
Pub/sub takes the same idea but encapsulates the observation logic inside a
|
||||||
|
separate object which manages all of its subscribers or listeners. Subscribers
|
||||||
|
are bound to an event name, and will only receive notifications of the events
|
||||||
|
they subscribed to.
|
||||||
|
|
||||||
|
**TLDR: What does evenement do, in short? It provides a mapping from event
|
||||||
|
names to a list of listener functions and triggers each listener for a given
|
||||||
|
event when it is emitted.**
|
||||||
|
|
||||||
|
Why do we do this, you ask? To achieve decoupling.
|
||||||
|
|
||||||
|
It allows you to design a system where the core will emit events, and modules
|
||||||
|
are able to subscribe to these events. And respond to them.
|
77
objects/evenement/evenement/doc/01-api.md
Normal file
77
objects/evenement/evenement/doc/01-api.md
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# API
|
||||||
|
|
||||||
|
The API that événement exposes is defined by the
|
||||||
|
`Evenement\EventEmitterInterface`. The interface is useful if you want to
|
||||||
|
define an interface that extends the emitter and implicitly defines certain
|
||||||
|
events to be emitted, or if you want to type hint an `EventEmitter` to be
|
||||||
|
passed to a method without coupling to the specific implementation.
|
||||||
|
|
||||||
|
## on($event, callable $listener)
|
||||||
|
|
||||||
|
Allows you to subscribe to an event.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$emitter->on('user.created', function (User $user) use ($logger) {
|
||||||
|
$logger->log(sprintf("User '%s' was created.", $user->getLogin()));
|
||||||
|
});
|
||||||
|
|
||||||
|
Since the listener can be any callable, you could also use an instance method
|
||||||
|
instead of the anonymous function:
|
||||||
|
|
||||||
|
$loggerSubscriber = new LoggerSubscriber($logger);
|
||||||
|
$emitter->on('user.created', array($loggerSubscriber, 'onUserCreated'));
|
||||||
|
|
||||||
|
This has the benefit that listener does not even need to know that the emitter
|
||||||
|
exists.
|
||||||
|
|
||||||
|
You can also accept more than one parameter for the listener:
|
||||||
|
|
||||||
|
$emitter->on('numbers_added', function ($result, $a, $b) {});
|
||||||
|
|
||||||
|
## once($event, callable $listener)
|
||||||
|
|
||||||
|
Convenience method that adds a listener which is guaranteed to only be called
|
||||||
|
once.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$conn->once('connected', function () use ($conn, $data) {
|
||||||
|
$conn->send($data);
|
||||||
|
});
|
||||||
|
|
||||||
|
## emit($event, array $arguments = [])
|
||||||
|
|
||||||
|
Emit an event, which will call all listeners.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$conn->emit('data', array($data));
|
||||||
|
|
||||||
|
The second argument to emit is an array of listener arguments. This is how you
|
||||||
|
specify more args:
|
||||||
|
|
||||||
|
$result = $a + $b;
|
||||||
|
$emitter->emit('numbers_added', array($result, $a, $b));
|
||||||
|
|
||||||
|
## listeners($event)
|
||||||
|
|
||||||
|
Allows you to inspect the listeners attached to an event. Particularly useful
|
||||||
|
to check if there are any listeners at all.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$e = new \RuntimeException('Everything is broken!');
|
||||||
|
if (0 === count($emitter->listeners('error'))) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
## removeListener($event, callable $listener)
|
||||||
|
|
||||||
|
Remove a specific listener for a specific event.
|
||||||
|
|
||||||
|
## removeAllListeners($event = null)
|
||||||
|
|
||||||
|
Remove all listeners for a specific event or all listeners alltogether. This
|
||||||
|
is useful for long-running processes, where you want to remove listeners in
|
||||||
|
order to allow them to get garbage collected.
|
155
objects/evenement/evenement/doc/02-plugin-system.md
Normal file
155
objects/evenement/evenement/doc/02-plugin-system.md
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
# Example: Plugin system
|
||||||
|
|
||||||
|
In this example I will show you how to create a generic plugin system with
|
||||||
|
événement where plugins can alter the behaviour of the app. The app is a blog.
|
||||||
|
Boring, I know. By using the EventEmitter it will be easy to extend this blog
|
||||||
|
with additional functionality without modifying the core system.
|
||||||
|
|
||||||
|
The blog is quite basic. Users are able to create blog posts when they log in.
|
||||||
|
The users are stored in a static config file, so there is no sign up process.
|
||||||
|
Once logged in they get a "new post" link which gives them a form where they
|
||||||
|
can create a new blog post with plain HTML. That will store the post in a
|
||||||
|
document database. The index lists all blog post titles by date descending.
|
||||||
|
Clicking on the post title will take you to the full post.
|
||||||
|
|
||||||
|
## Plugin structure
|
||||||
|
|
||||||
|
The goal of the plugin system is to allow features to be added to the blog
|
||||||
|
without modifying any core files of the blog.
|
||||||
|
|
||||||
|
The plugins are managed through a config file, `plugins.json`. This JSON file
|
||||||
|
contains a JSON-encoded list of class-names for plugin classes. This allows
|
||||||
|
you to enable and disable plugins in a central location. The initial
|
||||||
|
`plugins.json` is just an empty array:
|
||||||
|
```json
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
A plugin class must implement the `PluginInterface`:
|
||||||
|
```php
|
||||||
|
interface PluginInterface
|
||||||
|
{
|
||||||
|
function attachEvents(EventEmitterInterface $emitter);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `attachEvents` method allows the plugin to attach any events to the
|
||||||
|
emitter. For example:
|
||||||
|
```php
|
||||||
|
class FooPlugin implements PluginInterface
|
||||||
|
{
|
||||||
|
public function attachEvents(EventEmitterInterface $emitter)
|
||||||
|
{
|
||||||
|
$emitter->on('foo', function () {
|
||||||
|
echo 'bar!';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The blog system creates an emitter instance and loads the plugins:
|
||||||
|
```php
|
||||||
|
$emitter = new EventEmitter();
|
||||||
|
|
||||||
|
$pluginClasses = json_decode(file_get_contents('plugins.json'), true);
|
||||||
|
foreach ($pluginClasses as $pluginClass) {
|
||||||
|
$plugin = new $pluginClass();
|
||||||
|
$pluginClass->attachEvents($emitter);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the base system. There are no plugins yet, and there are no events yet
|
||||||
|
either. That's because I don't know which extension points will be needed. I
|
||||||
|
will add them on demand.
|
||||||
|
|
||||||
|
## Feature: Markdown
|
||||||
|
|
||||||
|
Writing blog posts in HTML sucks! Wouldn't it be great if I could write them
|
||||||
|
in a nice format such as markdown, and have that be converted to HTML for me?
|
||||||
|
|
||||||
|
This feature will need two extension points. I need to be able to mark posts
|
||||||
|
as markdown, and I need to be able to hook into the rendering of the post body
|
||||||
|
and convert it from markdown to HTML. So the blog needs two new events:
|
||||||
|
`post.create` and `post.render`.
|
||||||
|
|
||||||
|
In the code that creates the post, I'll insert the `post.create` event:
|
||||||
|
```php
|
||||||
|
class PostEvent
|
||||||
|
{
|
||||||
|
public $post;
|
||||||
|
|
||||||
|
public function __construct(array $post)
|
||||||
|
{
|
||||||
|
$this->post = $post;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = createPostFromRequest($_POST);
|
||||||
|
|
||||||
|
$event = new PostEvent($post);
|
||||||
|
$emitter->emit('post.create', array($event));
|
||||||
|
$post = $event->post;
|
||||||
|
|
||||||
|
$db->save('post', $post);
|
||||||
|
```
|
||||||
|
|
||||||
|
This shows that you can wrap a value in an event object to make it mutable,
|
||||||
|
allowing listeners to change it.
|
||||||
|
|
||||||
|
The same thing for the `post.render` event:
|
||||||
|
```php
|
||||||
|
public function renderPostBody(array $post)
|
||||||
|
{
|
||||||
|
$emitter = $this->emitter;
|
||||||
|
|
||||||
|
$event = new PostEvent($post);
|
||||||
|
$emitter->emit('post.render', array($event));
|
||||||
|
$post = $event->post;
|
||||||
|
|
||||||
|
return $post['body'];
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1><?= $post['title'] %></h1>
|
||||||
|
<p><?= renderPostBody($post) %></p>
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, the events are in place. It's time to create the first plugin, woohoo! I
|
||||||
|
will call this the `MarkdownPlugin`, so here's `plugins.json`:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"MarkdownPlugin"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about
|
||||||
|
including any files. I just have to worry about implementing the plugin class.
|
||||||
|
The `markdown` function represents a markdown to HTML converter.
|
||||||
|
```php
|
||||||
|
class MarkdownPlugin implements PluginInterface
|
||||||
|
{
|
||||||
|
public function attachEvents(EventEmitterInterface $emitter)
|
||||||
|
{
|
||||||
|
$emitter->on('post.create', function (PostEvent $event) {
|
||||||
|
$event->post['format'] = 'markdown';
|
||||||
|
});
|
||||||
|
|
||||||
|
$emitter->on('post.render', function (PostEvent $event) {
|
||||||
|
if (isset($event->post['format']) && 'markdown' === $event->post['format']) {
|
||||||
|
$event->post['body'] = markdown($event->post['body']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There you go, the blog now renders posts as markdown. But all of the previous
|
||||||
|
posts before the addition of the markdown plugin are still rendered correctly
|
||||||
|
as raw HTML.
|
||||||
|
|
||||||
|
## Feature: Comments
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## Feature: Comment spam control
|
||||||
|
|
||||||
|
TODO
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Evenement.
|
||||||
|
*
|
||||||
|
* (c) Igor Wiedler <igor@wiedler.ch>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ITERATIONS = 10000000;
|
||||||
|
|
||||||
|
use Evenement\EventEmitter;
|
||||||
|
|
||||||
|
require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$emitter = new EventEmitter();
|
||||||
|
|
||||||
|
$emitter->on('event', function () {});
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
for ($i = 0; $i < ITERATIONS; $i++) {
|
||||||
|
$emitter->emit('event');
|
||||||
|
}
|
||||||
|
$time = microtime(true) - $start;
|
||||||
|
|
||||||
|
echo 'Emitting ', number_format(ITERATIONS), ' events took: ', number_format($time, 2), 's', PHP_EOL;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue