mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 01:39:24 +02:00
Noving composer into vendor folder
This commit is contained in:
parent
37880a28cc
commit
f1b6d0af52
26137 changed files with 803896 additions and 1910503 deletions
|
@ -9,7 +9,7 @@ RUN apt-get install apt-transport-https lsb-release logrotate git curl vim net-t
|
|||
# Install all the rest
|
||||
RUN DEBIAN_FRONTEND="noninteractive" apt-get install wget sshpass nano net-tools curl apache2 php7.4 libapache2-mod-php7.4 php7.4-mysql php7.4-curl php7.4-gd php7.4-intl php7.4-xml apache2 mysql-server mysql-client ffmpeg git libimage-exiftool-perl libapache2-mod-xsendfile python build-essential make libpcre3 libpcre3-dev libssl-dev unzip htop python3-pip -y
|
||||
|
||||
RUN cd /var/www/html && git clone https://github.com/WWBN/AVideo.git && cd /var/www/html && git clone https://github.com/WWBN/AVideo-Encoder.git && curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && chmod a+rx /usr/local/bin/youtube-dl && chown www-data:www-data /var/www/html/AVideo/plugin && chmod 755 /var/www/html/AVideo/plugin && curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && chmod a+rx /usr/local/bin/youtube-dl && a2enmod rewrite && chown www-data:www-data /var/www/html/AVideo/plugin && chmod 755 /var/www/html/AVideo/plugin && cd /var/www/html/AVideo/plugin/User_Location/install && unzip install.zip && pip3 install youtube-dl && pip3 install --upgrade youtube-dl && a2enmod expires && a2enmod headers && chmod 777 /var/www/html/AVideo/objects/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/ && mkdir /var/www/tmp && chmod 777 /var/www/tmp
|
||||
RUN cd /var/www/html && git clone https://github.com/WWBN/AVideo.git && cd /var/www/html && git clone https://github.com/WWBN/AVideo-Encoder.git && curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && chmod a+rx /usr/local/bin/youtube-dl && chown www-data:www-data /var/www/html/AVideo/plugin && chmod 755 /var/www/html/AVideo/plugin && curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && chmod a+rx /usr/local/bin/youtube-dl && a2enmod rewrite && chown www-data:www-data /var/www/html/AVideo/plugin && chmod 755 /var/www/html/AVideo/plugin && cd /var/www/html/AVideo/plugin/User_Location/install && unzip install.zip && pip3 install youtube-dl && pip3 install --upgrade youtube-dl && a2enmod expires && a2enmod headers && chmod 777 /var/www/html/AVideo/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/ && mkdir /var/www/tmp && chmod 777 /var/www/tmp
|
||||
|
||||
# RUN mysql -u root -e "USE mysql;CREATE USER 'youphptube'@'localhost' IDENTIFIED BY 'youphptube';GRANT ALL PRIVILEGES ON *.* TO 'youphptube'@'localhost'; FLUSH PRIVILEGES; "
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"description": "Audio Video Platform",
|
||||
"type": "project",
|
||||
"config": {
|
||||
"vendor-dir": "objects",
|
||||
"vendor-dir": "vendor",
|
||||
"platform": {
|
||||
"php": "7.4.3"
|
||||
}
|
||||
|
|
47
composer.lock
generated
47
composer.lock
generated
|
@ -993,16 +993,16 @@
|
|||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.8.2",
|
||||
"version": "1.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "dc960a912984efb74d0a90222870c72c87f10c91"
|
||||
"reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
|
||||
"reference": "dc960a912984efb74d0a90222870c72c87f10c91",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85",
|
||||
"reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1039,13 +1039,34 @@
|
|||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "George Mponos",
|
||||
"email": "gmponos@gmail.com",
|
||||
"homepage": "https://github.com/gmponos"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com",
|
||||
"homepage": "https://github.com/Nyholm"
|
||||
},
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com",
|
||||
"homepage": "https://github.com/sagikazarmark"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"email": "webmaster@tubo-world.de",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
|
@ -1062,9 +1083,23 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/1.8.2"
|
||||
"source": "https://github.com/guzzle/psr7/tree/1.8.3"
|
||||
},
|
||||
"time": "2021-04-26T09:17:50+00:00"
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/Nyholm",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-05T13:56:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hybridauth/hybridauth",
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit78837c7a9c090d873268c14c1a5daf3d::getLoader();
|
||||
|
|
5
objects/cboden/ratchet/.gitignore
vendored
5
objects/cboden/ratchet/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
|||
phpunit.xml
|
||||
reports
|
||||
sandbox
|
||||
vendor
|
||||
composer.lock
|
|
@ -1,15 +0,0 @@
|
|||
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
|
|
@ -1,145 +0,0 @@
|
|||
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
|
|
@ -1,19 +0,0 @@
|
|||
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.
|
|
@ -1,42 +0,0 @@
|
|||
# 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 \
|
|
@ -1,83 +0,0 @@
|
|||
# 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!'); };
|
||||
```
|
|
@ -1,39 +0,0 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<?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>
|
|
@ -1,41 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?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);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<?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();
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?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);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?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) {
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet;
|
||||
|
||||
interface MessageComponentInterface extends ComponentInterface, MessageInterface {
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?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);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet\Session\Serialize;
|
||||
|
||||
interface HandlerInterface {
|
||||
/**
|
||||
* @param array
|
||||
* @return string
|
||||
*/
|
||||
function serialize(array $data);
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
function unserialize($raw);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
<?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");
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet\Wamp;
|
||||
|
||||
class Exception extends \Exception {
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
<?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()]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<?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);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||
|
||||
interface MessageCallableInterface {
|
||||
public function onMessage(ConnectionInterface $conn, MessageInterface $msg);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet\WebSocket;
|
||||
use Ratchet\ComponentInterface;
|
||||
|
||||
interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface {
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<?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();
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?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();
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"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": {}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"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": {}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"options": {"failByDrop": false}
|
||||
, "outdir": "reports/rfc"
|
||||
|
||||
, "servers": [
|
||||
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||
]
|
||||
|
||||
, "cases": ["*"]
|
||||
, "exclude-cases": []
|
||||
, "exclude-agent-cases": {}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
|
||||
$loader = require __DIR__ . '/../vendor/autoload.php';
|
||||
$loader->addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet');
|
|
@ -1,50 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet\Wamp\Stub;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
use Ratchet\Wamp\WampServerInterface;
|
||||
|
||||
interface WsWampServerInterface extends WsServerInterface, WampServerInterface {
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
namespace Ratchet\WebSocket\Stub;
|
||||
use Ratchet\MessageComponentInterface;
|
||||
use Ratchet\WebSocket\WsServerInterface;
|
||||
|
||||
interface WsMessageComponentInterface extends MessageComponentInterface, WsServerInterface {
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
<?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");
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?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!');
|
||||
}
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?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'));
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
<?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));
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
<?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());
|
||||
}
|
||||
}
|
|
@ -1,295 +0,0 @@
|
|||
<?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']));
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
<?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());
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
<?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'));
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?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')));
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
PHP ICO - The PHP ICO Generator
|
||||
===============================
|
||||
|
||||
PHP ICO provides an easy-to-use PHP class that generates valid [ICO files](http://en.wikipedia.org/wiki/ICO_%28file_format%29). Note that these are not simply BMP, GIF, or PNG formatted images with an extension of ico, the images generated by this class are fully valid ICO-formatted image files.
|
||||
|
||||
The class uses the GD library for reading an image from the source file and uses pure PHP for generating the ICO file format. In theory, any image format that GD can read can be used as a source image to generate the ICO file. I tested this library with JPEG, GIF, and PNG images with great results. If an animated GIF is supplied, the first frame will be used to generate the ICO image.
|
||||
|
||||
The PHP ICO library is available on Composer via Packagist at `chrisjean/php-ico`.
|
||||
|
||||
ICO Format Details
|
||||
------------------
|
||||
|
||||
The primary goal of creating this class was to have a simple-to-use and reliable method of generating ICO files for use as [favicons](http://en.wikipedia.org/wiki/Favicon) in websites. This goal drove much of the development and is the reason for some of the limitations in the resulting functionality.
|
||||
|
||||
ICO files support two different ways of encoding the actual image data: the [Windows BMP](http://en.wikipedia.org/wiki/BMP_file_format) format and the [PNG](http://en.wikipedia.org/wiki/Portable_Network_Graphics) format. Since support for the PNG format was introduced in Windows Vista and both older and newer versions of Windows support the BMP enocoding, the BMP encoding method was used.
|
||||
|
||||
Images are encoded using 32 bits per pixel. This allows for alpha channel information in the resulting images. Support for 32 bit images was added in Windows XP. This means that older versions of Windows are not likely to display the ICO images properly, if at all. The generated images have not been tested on versions of Windows predating XP.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The following is a very basic example of using the PHP ICO library:
|
||||
|
||||
```php
|
||||
require( dirname( __FILE__ ) . '/class-php-ico.php' );
|
||||
|
||||
$source = dirname( __FILE__ ) . '/example.gif';
|
||||
$destination = dirname( __FILE__ ) . '/example.ico';
|
||||
|
||||
$ico_lib = new PHP_ICO( $source );
|
||||
$ico_lib->save_ico( $destination );
|
||||
````
|
||||
|
||||
It takes a source file named `example.gif` and produce an output ICO file named `example.ico`. `example.ico` will contain a single image that is the same size as the source image.
|
||||
|
||||
The ICO file format is capable of holding multiple images, each of a different size. The PHP ICO library opens up this feature of the ICO format as shown in the following example:
|
||||
|
||||
```php
|
||||
require( dirname( __FILE__ ) . '/class-php-ico.php' );
|
||||
|
||||
$source = dirname( __FILE__ ) . '/example.gif';
|
||||
$destination = dirname( __FILE__ ) . '/example.ico';
|
||||
|
||||
$ico_lib = new PHP_ICO( $source, array( array( 32, 32 ), array( 64, 64 ) ) );
|
||||
$ico_lib->save_ico( $destination );
|
||||
```
|
||||
|
||||
As with the previous example, this example produces `example.ico` from the `example.gif` source file. In this example, sizes were passed to the constructor that result in the `example.ico` file containing two images: one that is 32x32 and one that is 64x64. Since the same source image is used for each of the contained images, each contained image is simply the source image scaled to the new dimensions.
|
||||
|
||||
Using different source images for the different sizes contained inside an ICO file can create much better results. For instance, you can use a high-quality image for higher-resolution images and use smaller images that are tailored for their specific dimensions for the smaller sizes. The following example shows how the PHP ICO library can be used to create such ICO files:
|
||||
|
||||
```php
|
||||
require( dirname( __FILE__ ) . '/class-php-ico.php' );
|
||||
|
||||
$destination = dirname( __FILE__ ) . '/example.ico';
|
||||
|
||||
$ico_lib = new PHP_ICO();
|
||||
|
||||
$ico_lib->add_image( dirname( __FILE__ ) . '/example-small.gif', array( array( 16, 16 ), array( 24, 24 ), array( 32, 32 ) ) );
|
||||
$ico_lib->add_image( dirname( __FILE__ ) . '/example-medium.gif', array( array( 48, 48 ), array( 96, 96 ) ) );
|
||||
$ico_lib->add_image( dirname( __FILE__ ) . '/example-large.gif', array( array( 128, 128 ) ) );
|
||||
|
||||
$ico_lib->save_ico( $destination );
|
||||
```
|
||||
|
||||
This example creates a single ICO file named `example.ico` just as the previous examples. The difference is that this example used multiple source images to generate the final ICO images. The final ICO image contains a total of six images: 16x16, 24x24, and 32x32 images from `example-small.gif`; 48x48 and 96x96 images from `example-medium.gif`; and a 128x128 image from `example-large.gif`.
|
||||
|
||||
By using this feature of supplying multiple source images with specific sizes for each, you can generate ICO files that have very high-quality images at each supplied resolution.
|
||||
|
||||
Since the PHP ICO library was created to generate favicon files, the following example shows how to generate a favicon ICO file with all the needed dimensions from a single source image:
|
||||
|
||||
```php
|
||||
require( dirname( __FILE__ ) . '/class-php-ico.php' );
|
||||
|
||||
$source = dirname( __FILE__ ) . '/example.gif';
|
||||
$destination = dirname( __FILE__ ) . '/example.ico';
|
||||
|
||||
$sizes = array(
|
||||
array( 16, 16 ),
|
||||
array( 24, 24 ),
|
||||
array( 32, 32 ),
|
||||
array( 48, 48 ),
|
||||
);
|
||||
|
||||
$ico_lib = new PHP_ICO( $source, $sizes );
|
||||
$ico_lib->save_ico( $destination );
|
||||
```
|
||||
|
||||
I've found that the 16x16, 24x24, 32x32, and 48x48 image sizes cover all the sizes that browsers and Windows will try to use a favicon image for. The different sizes come from using the favicon for various browser icons, bookmarks, and various other places.
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
I'd like to thank [iThemes](http://ithemes.com) for making this project possible. This code was originally developed to add easy-to-use favicon support to the [Builder theme](http://ithemes.com/purchase/builder-theme/). I asked [Cory](http://corymiller.tv/), owner of iThemes and my boss, if I could share the final code. He gave me the green light. Win for everyone. :)
|
||||
|
||||
Thanks iThemes. Thanks Cory.
|
|
@ -1,11 +0,0 @@
|
|||
1.0.0 - 2011-08-04
|
||||
Initial version.
|
||||
1.0.1 - 2012-10-01
|
||||
Added requirements checks.
|
||||
1.0.2 - 2013-01-07
|
||||
Added bug fix that caused source images with transparency to have artifacts when output at the same dimensions as the source.
|
||||
1.0.3 - 2016-07-22
|
||||
Updated class constructor to __construct() as PHP4-style constructors are now deprecated. Props @nuxodin.
|
||||
Removed trailing whitespace from blank lines.
|
||||
1.0.4 - 2016-09-27
|
||||
Updated version to get composer to track properly.
|
|
@ -1,264 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2011-2016 Chris Jean & iThemes
|
||||
Licensed under GPLv2 or above
|
||||
|
||||
Version 1.0.4
|
||||
*/
|
||||
|
||||
class PHP_ICO {
|
||||
/**
|
||||
* Images in the BMP format.
|
||||
*
|
||||
* @var array
|
||||
* @access private
|
||||
*/
|
||||
var $_images = array();
|
||||
|
||||
/**
|
||||
* Flag to tell if the required functions exist.
|
||||
*
|
||||
* @var boolean
|
||||
* @access private
|
||||
*/
|
||||
var $_has_requirements = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor - Create a new ICO generator.
|
||||
*
|
||||
* If the constructor is not passed a file, a file will need to be supplied using the {@link PHP_ICO::add_image}
|
||||
* function in order to generate an ICO file.
|
||||
*
|
||||
* @param string $file Optional. Path to the source image file.
|
||||
* @param array $sizes Optional. An array of sizes (each size is an array with a width and height) that the source image should be rendered at in the generated ICO file. If sizes are not supplied, the size of the source image will be used.
|
||||
*/
|
||||
function __construct( $file = false, $sizes = array() ) {
|
||||
$required_functions = array(
|
||||
'getimagesize',
|
||||
'imagecreatefromstring',
|
||||
'imagecreatetruecolor',
|
||||
'imagecolortransparent',
|
||||
'imagecolorallocatealpha',
|
||||
'imagealphablending',
|
||||
'imagesavealpha',
|
||||
'imagesx',
|
||||
'imagesy',
|
||||
'imagecopyresampled',
|
||||
);
|
||||
|
||||
foreach ( $required_functions as $function ) {
|
||||
if ( ! function_exists( $function ) ) {
|
||||
trigger_error( "The PHP_ICO class was unable to find the $function function, which is part of the GD library. Ensure that the system has the GD library installed and that PHP has access to it through a PHP interface, such as PHP's GD module. Since this function was not found, the library will be unable to create ICO files." );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_has_requirements = true;
|
||||
|
||||
|
||||
if ( false != $file )
|
||||
$this->add_image( $file, $sizes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an image to the generator.
|
||||
*
|
||||
* This function adds a source image to the generator. It serves two main purposes: add a source image if one was
|
||||
* not supplied to the constructor and to add additional source images so that different images can be supplied for
|
||||
* different sized images in the resulting ICO file. For instance, a small source image can be used for the small
|
||||
* resolutions while a larger source image can be used for large resolutions.
|
||||
*
|
||||
* @param string $file Path to the source image file.
|
||||
* @param array $sizes Optional. An array of sizes (each size is an array with a width and height) that the source image should be rendered at in the generated ICO file. If sizes are not supplied, the size of the source image will be used.
|
||||
* @return boolean true on success and false on failure.
|
||||
*/
|
||||
function add_image( $file, $sizes = array() ) {
|
||||
if ( ! $this->_has_requirements )
|
||||
return false;
|
||||
|
||||
if ( false === ( $im = $this->_load_image_file( $file ) ) )
|
||||
return false;
|
||||
|
||||
|
||||
if ( empty( $sizes ) )
|
||||
$sizes = array( imagesx( $im ), imagesy( $im ) );
|
||||
|
||||
// If just a single size was passed, put it in array.
|
||||
if ( ! is_array( $sizes[0] ) )
|
||||
$sizes = array( $sizes );
|
||||
|
||||
foreach ( (array) $sizes as $size ) {
|
||||
list( $width, $height ) = $size;
|
||||
|
||||
$new_im = imagecreatetruecolor( $width, $height );
|
||||
|
||||
imagecolortransparent( $new_im, imagecolorallocatealpha( $new_im, 0, 0, 0, 127 ) );
|
||||
imagealphablending( $new_im, false );
|
||||
imagesavealpha( $new_im, true );
|
||||
|
||||
$source_width = imagesx( $im );
|
||||
$source_height = imagesy( $im );
|
||||
|
||||
if ( false === imagecopyresampled( $new_im, $im, 0, 0, 0, 0, $width, $height, $source_width, $source_height ) )
|
||||
continue;
|
||||
|
||||
$this->_add_image_data( $new_im );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the ICO file data to a file path.
|
||||
*
|
||||
* @param string $file Path to save the ICO file data into.
|
||||
* @return boolean true on success and false on failure.
|
||||
*/
|
||||
function save_ico( $file ) {
|
||||
if ( ! $this->_has_requirements )
|
||||
return false;
|
||||
|
||||
if ( false === ( $data = $this->_get_ico_data() ) )
|
||||
return false;
|
||||
|
||||
if ( false === ( $fh = fopen( $file, 'w' ) ) )
|
||||
return false;
|
||||
|
||||
if ( false === ( fwrite( $fh, $data ) ) ) {
|
||||
fclose( $fh );
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose( $fh );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the final ICO data by creating a file header and adding the image data.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function _get_ico_data() {
|
||||
if ( ! is_array( $this->_images ) || empty( $this->_images ) )
|
||||
return false;
|
||||
|
||||
|
||||
$data = pack( 'vvv', 0, 1, count( $this->_images ) );
|
||||
$pixel_data = '';
|
||||
|
||||
$icon_dir_entry_size = 16;
|
||||
|
||||
$offset = 6 + ( $icon_dir_entry_size * count( $this->_images ) );
|
||||
|
||||
foreach ( $this->_images as $image ) {
|
||||
$data .= pack( 'CCCCvvVV', $image['width'], $image['height'], $image['color_palette_colors'], 0, 1, $image['bits_per_pixel'], $image['size'], $offset );
|
||||
$pixel_data .= $image['data'];
|
||||
|
||||
$offset += $image['size'];
|
||||
}
|
||||
|
||||
$data .= $pixel_data;
|
||||
unset( $pixel_data );
|
||||
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a GD image resource and change it into a raw BMP format.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function _add_image_data( $im ) {
|
||||
$width = imagesx( $im );
|
||||
$height = imagesy( $im );
|
||||
|
||||
|
||||
$pixel_data = array();
|
||||
|
||||
$opacity_data = array();
|
||||
$current_opacity_val = 0;
|
||||
|
||||
for ( $y = $height - 1; $y >= 0; $y-- ) {
|
||||
for ( $x = 0; $x < $width; $x++ ) {
|
||||
$color = imagecolorat( $im, $x, $y );
|
||||
|
||||
$alpha = ( $color & 0x7F000000 ) >> 24;
|
||||
$alpha = ( 1 - ( $alpha / 127 ) ) * 255;
|
||||
|
||||
$color &= 0xFFFFFF;
|
||||
$color |= 0xFF000000 & ( $alpha << 24 );
|
||||
|
||||
$pixel_data[] = $color;
|
||||
|
||||
|
||||
$opacity = ( $alpha <= 127 ) ? 1 : 0;
|
||||
|
||||
$current_opacity_val = ( $current_opacity_val << 1 ) | $opacity;
|
||||
|
||||
if ( ( ( $x + 1 ) % 32 ) == 0 ) {
|
||||
$opacity_data[] = $current_opacity_val;
|
||||
$current_opacity_val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( $x % 32 ) > 0 ) {
|
||||
while ( ( $x++ % 32 ) > 0 )
|
||||
$current_opacity_val = $current_opacity_val << 1;
|
||||
|
||||
$opacity_data[] = $current_opacity_val;
|
||||
$current_opacity_val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$image_header_size = 40;
|
||||
$color_mask_size = $width * $height * 4;
|
||||
$opacity_mask_size = ( ceil( $width / 32 ) * 4 ) * $height;
|
||||
|
||||
|
||||
$data = pack( 'VVVvvVVVVVV', 40, $width, ( $height * 2 ), 1, 32, 0, 0, 0, 0, 0, 0 );
|
||||
|
||||
foreach ( $pixel_data as $color )
|
||||
$data .= pack( 'V', $color );
|
||||
|
||||
foreach ( $opacity_data as $opacity )
|
||||
$data .= pack( 'N', $opacity );
|
||||
|
||||
|
||||
$image = array(
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'color_palette_colors' => 0,
|
||||
'bits_per_pixel' => 32,
|
||||
'size' => $image_header_size + $color_mask_size + $opacity_mask_size,
|
||||
'data' => $data,
|
||||
);
|
||||
|
||||
$this->_images[] = $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in the source image file and convert it into a GD image resource.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function _load_image_file( $file ) {
|
||||
// Run a cheap check to verify that it is an image file.
|
||||
if ( false === ( $size = getimagesize( $file ) ) )
|
||||
return false;
|
||||
|
||||
if ( false === ( $file_data = file_get_contents( $file ) ) )
|
||||
return false;
|
||||
|
||||
if ( false === ( $im = imagecreatefromstring( $file_data ) ) )
|
||||
return false;
|
||||
|
||||
unset( $file_data );
|
||||
|
||||
|
||||
return $im;
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"name": "chrisjean/php-ico",
|
||||
"description": "An easy-to-use library to generate valid ICO files.",
|
||||
"version": "1.0.4",
|
||||
"keywords": ["ico", "favicon"],
|
||||
"homepage": "https://github.com/chrisbliss18/php-ico",
|
||||
"license": "GPL-2.0+",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Chris Jean",
|
||||
"homepage": "https://chrisjean.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/chrisbliss18/php-ico/issues",
|
||||
"source": "https://github.com/chrisbliss18/php-ico"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.4",
|
||||
"ext-gd": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"class-php-ico.php"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,359 +0,0 @@
|
|||
PHP ICO - The PHP ICO Generator
|
||||
|
||||
Copyright 2011-2016 Chris Jean
|
||||
|
||||
PHP ICO is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
PHP ICO is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with PHP ICO; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
CREDITS
|
||||
|
||||
Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks
|
||||
to the DevNetwork Community for their help (see docs/ref-devnetwork.html for
|
||||
more details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake
|
||||
for letting me package his fantastic XSS cheatsheet for a smoketest.
|
||||
|
||||
vim: et sw=4 sts=4
|
|
@ -1,504 +0,0 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
vim: et sw=4 sts=4
|
|
@ -1,29 +0,0 @@
|
|||
HTML Purifier [](http://travis-ci.org/ezyang/htmlpurifier)
|
||||
=============
|
||||
|
||||
HTML Purifier is an HTML filtering solution that uses a unique combination
|
||||
of robust whitelists and aggressive parsing to ensure that not only are
|
||||
XSS attacks thwarted, but the resulting HTML is standards compliant.
|
||||
|
||||
HTML Purifier is oriented towards richly formatted documents from
|
||||
untrusted sources that require CSS and a full tag-set. This library can
|
||||
be configured to accept a more restrictive set of tags, but it won't be
|
||||
as efficient as more bare-bones parsers. It will, however, do the job
|
||||
right, which may be more important.
|
||||
|
||||
Places to go:
|
||||
|
||||
* See INSTALL for a quick installation guide
|
||||
* See docs/ for developer-oriented documentation, code examples and
|
||||
an in-depth installation guide.
|
||||
* See WYSIWYG for information on editors like TinyMCE and FCKeditor
|
||||
|
||||
HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)
|
||||
|
||||
## Installation
|
||||
|
||||
Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).
|
||||
|
||||
If you're using Composer to manage dependencies, you can use
|
||||
|
||||
$ composer require ezyang/htmlpurifier
|
|
@ -1 +0,0 @@
|
|||
4.13.0
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"description": "Standards compliant HTML filter written in PHP",
|
||||
"type": "library",
|
||||
"keywords": ["html"],
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"license": "LGPL-2.1-or-later",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Edward Z. Yang",
|
||||
"email": "admin@htmlpurifier.org",
|
||||
"homepage": "http://ezyang.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "HTMLPurifier": "library/" },
|
||||
"files": ["library/HTMLPurifier.composer.php"],
|
||||
"exclude-from-classmap": [
|
||||
"/library/HTMLPurifier/Language/"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This is a stub include that automatically configures the include path.
|
||||
*/
|
||||
|
||||
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
|
||||
require_once 'HTMLPurifier/Bootstrap.php';
|
||||
require_once 'HTMLPurifier.autoload.php';
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Legacy autoloader for systems lacking spl_autoload_register
|
||||
*
|
||||
* Must be separate to prevent deprecation warning on PHP 7.2
|
||||
*/
|
||||
|
||||
function __autoload($class)
|
||||
{
|
||||
return HTMLPurifier_Bootstrap::autoload($class);
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Convenience file that registers autoload handler for HTML Purifier.
|
||||
* It also does some sanity checks.
|
||||
*/
|
||||
|
||||
if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
|
||||
// We need unregister for our pre-registering functionality
|
||||
HTMLPurifier_Bootstrap::registerAutoload();
|
||||
if (function_exists('__autoload')) {
|
||||
// Be polite and ensure that userland autoload gets retained
|
||||
spl_autoload_register('__autoload');
|
||||
}
|
||||
} elseif (!function_exists('__autoload')) {
|
||||
require dirname(__FILE__) . '/HTMLPurifier.autoload-legacy.php';
|
||||
}
|
||||
|
||||
if (ini_get('zend.ze1_compatibility_mode')) {
|
||||
trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
if (!defined('HTMLPURIFIER_PREFIX')) {
|
||||
define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines a function wrapper for HTML Purifier for quick use.
|
||||
* @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
|
||||
*/
|
||||
|
||||
/**
|
||||
* Purify HTML.
|
||||
* @param string $html String HTML to purify
|
||||
* @param mixed $config Configuration to use, can be any value accepted by
|
||||
* HTMLPurifier_Config::create()
|
||||
* @return string
|
||||
*/
|
||||
function HTMLPurifier($html, $config = null)
|
||||
{
|
||||
static $purifier = false;
|
||||
if (!$purifier) {
|
||||
$purifier = new HTMLPurifier();
|
||||
}
|
||||
return $purifier->purify($html, $config);
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,234 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file was auto-generated by generate-includes.php and includes all of
|
||||
* the core files required by HTML Purifier. Use this if performance is a
|
||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
|
||||
* FILE, changes will be overwritten the next time the script is run.
|
||||
*
|
||||
* @version 4.13.0
|
||||
*
|
||||
* @warning
|
||||
* You must *not* include any other HTML Purifier files before this file,
|
||||
* because 'require' not 'require_once' is used.
|
||||
*
|
||||
* @warning
|
||||
* This file requires that the include path contains the HTML Purifier
|
||||
* library directory; this is not auto-set.
|
||||
*/
|
||||
|
||||
require 'HTMLPurifier.php';
|
||||
require 'HTMLPurifier/Arborize.php';
|
||||
require 'HTMLPurifier/AttrCollections.php';
|
||||
require 'HTMLPurifier/AttrDef.php';
|
||||
require 'HTMLPurifier/AttrTransform.php';
|
||||
require 'HTMLPurifier/AttrTypes.php';
|
||||
require 'HTMLPurifier/AttrValidator.php';
|
||||
require 'HTMLPurifier/Bootstrap.php';
|
||||
require 'HTMLPurifier/Definition.php';
|
||||
require 'HTMLPurifier/CSSDefinition.php';
|
||||
require 'HTMLPurifier/ChildDef.php';
|
||||
require 'HTMLPurifier/Config.php';
|
||||
require 'HTMLPurifier/ConfigSchema.php';
|
||||
require 'HTMLPurifier/ContentSets.php';
|
||||
require 'HTMLPurifier/Context.php';
|
||||
require 'HTMLPurifier/DefinitionCache.php';
|
||||
require 'HTMLPurifier/DefinitionCacheFactory.php';
|
||||
require 'HTMLPurifier/Doctype.php';
|
||||
require 'HTMLPurifier/DoctypeRegistry.php';
|
||||
require 'HTMLPurifier/ElementDef.php';
|
||||
require 'HTMLPurifier/Encoder.php';
|
||||
require 'HTMLPurifier/EntityLookup.php';
|
||||
require 'HTMLPurifier/EntityParser.php';
|
||||
require 'HTMLPurifier/ErrorCollector.php';
|
||||
require 'HTMLPurifier/ErrorStruct.php';
|
||||
require 'HTMLPurifier/Exception.php';
|
||||
require 'HTMLPurifier/Filter.php';
|
||||
require 'HTMLPurifier/Generator.php';
|
||||
require 'HTMLPurifier/HTMLDefinition.php';
|
||||
require 'HTMLPurifier/HTMLModule.php';
|
||||
require 'HTMLPurifier/HTMLModuleManager.php';
|
||||
require 'HTMLPurifier/IDAccumulator.php';
|
||||
require 'HTMLPurifier/Injector.php';
|
||||
require 'HTMLPurifier/Language.php';
|
||||
require 'HTMLPurifier/LanguageFactory.php';
|
||||
require 'HTMLPurifier/Length.php';
|
||||
require 'HTMLPurifier/Lexer.php';
|
||||
require 'HTMLPurifier/Node.php';
|
||||
require 'HTMLPurifier/PercentEncoder.php';
|
||||
require 'HTMLPurifier/PropertyList.php';
|
||||
require 'HTMLPurifier/PropertyListIterator.php';
|
||||
require 'HTMLPurifier/Queue.php';
|
||||
require 'HTMLPurifier/Strategy.php';
|
||||
require 'HTMLPurifier/StringHash.php';
|
||||
require 'HTMLPurifier/StringHashParser.php';
|
||||
require 'HTMLPurifier/TagTransform.php';
|
||||
require 'HTMLPurifier/Token.php';
|
||||
require 'HTMLPurifier/TokenFactory.php';
|
||||
require 'HTMLPurifier/URI.php';
|
||||
require 'HTMLPurifier/URIDefinition.php';
|
||||
require 'HTMLPurifier/URIFilter.php';
|
||||
require 'HTMLPurifier/URIParser.php';
|
||||
require 'HTMLPurifier/URIScheme.php';
|
||||
require 'HTMLPurifier/URISchemeRegistry.php';
|
||||
require 'HTMLPurifier/UnitConverter.php';
|
||||
require 'HTMLPurifier/VarParser.php';
|
||||
require 'HTMLPurifier/VarParserException.php';
|
||||
require 'HTMLPurifier/Zipper.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS.php';
|
||||
require 'HTMLPurifier/AttrDef/Clone.php';
|
||||
require 'HTMLPurifier/AttrDef/Enum.php';
|
||||
require 'HTMLPurifier/AttrDef/Integer.php';
|
||||
require 'HTMLPurifier/AttrDef/Lang.php';
|
||||
require 'HTMLPurifier/AttrDef/Switch.php';
|
||||
require 'HTMLPurifier/AttrDef/Text.php';
|
||||
require 'HTMLPurifier/AttrDef/URI.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Number.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Background.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Border.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Color.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Composite.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Filter.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Font.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Ident.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Length.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
|
||||
require 'HTMLPurifier/AttrDef/CSS/URI.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/Bool.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/Class.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/Color.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/ID.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/Length.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
|
||||
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
|
||||
require 'HTMLPurifier/AttrDef/URI/Email.php';
|
||||
require 'HTMLPurifier/AttrDef/URI/Host.php';
|
||||
require 'HTMLPurifier/AttrDef/URI/IPv4.php';
|
||||
require 'HTMLPurifier/AttrDef/URI/IPv6.php';
|
||||
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
|
||||
require 'HTMLPurifier/AttrTransform/Background.php';
|
||||
require 'HTMLPurifier/AttrTransform/BdoDir.php';
|
||||
require 'HTMLPurifier/AttrTransform/BgColor.php';
|
||||
require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
|
||||
require 'HTMLPurifier/AttrTransform/Border.php';
|
||||
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
|
||||
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
|
||||
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
|
||||
require 'HTMLPurifier/AttrTransform/Input.php';
|
||||
require 'HTMLPurifier/AttrTransform/Lang.php';
|
||||
require 'HTMLPurifier/AttrTransform/Length.php';
|
||||
require 'HTMLPurifier/AttrTransform/Name.php';
|
||||
require 'HTMLPurifier/AttrTransform/NameSync.php';
|
||||
require 'HTMLPurifier/AttrTransform/Nofollow.php';
|
||||
require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
|
||||
require 'HTMLPurifier/AttrTransform/SafeObject.php';
|
||||
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
|
||||
require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
|
||||
require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
|
||||
require 'HTMLPurifier/AttrTransform/Textarea.php';
|
||||
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
||||
require 'HTMLPurifier/ChildDef/Custom.php';
|
||||
require 'HTMLPurifier/ChildDef/Empty.php';
|
||||
require 'HTMLPurifier/ChildDef/List.php';
|
||||
require 'HTMLPurifier/ChildDef/Required.php';
|
||||
require 'HTMLPurifier/ChildDef/Optional.php';
|
||||
require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
|
||||
require 'HTMLPurifier/ChildDef/Table.php';
|
||||
require 'HTMLPurifier/DefinitionCache/Decorator.php';
|
||||
require 'HTMLPurifier/DefinitionCache/Null.php';
|
||||
require 'HTMLPurifier/DefinitionCache/Serializer.php';
|
||||
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
|
||||
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
|
||||
require 'HTMLPurifier/HTMLModule/Bdo.php';
|
||||
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
|
||||
require 'HTMLPurifier/HTMLModule/Edit.php';
|
||||
require 'HTMLPurifier/HTMLModule/Forms.php';
|
||||
require 'HTMLPurifier/HTMLModule/Hypertext.php';
|
||||
require 'HTMLPurifier/HTMLModule/Iframe.php';
|
||||
require 'HTMLPurifier/HTMLModule/Image.php';
|
||||
require 'HTMLPurifier/HTMLModule/Legacy.php';
|
||||
require 'HTMLPurifier/HTMLModule/List.php';
|
||||
require 'HTMLPurifier/HTMLModule/Name.php';
|
||||
require 'HTMLPurifier/HTMLModule/Nofollow.php';
|
||||
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
|
||||
require 'HTMLPurifier/HTMLModule/Object.php';
|
||||
require 'HTMLPurifier/HTMLModule/Presentation.php';
|
||||
require 'HTMLPurifier/HTMLModule/Proprietary.php';
|
||||
require 'HTMLPurifier/HTMLModule/Ruby.php';
|
||||
require 'HTMLPurifier/HTMLModule/SafeEmbed.php';
|
||||
require 'HTMLPurifier/HTMLModule/SafeObject.php';
|
||||
require 'HTMLPurifier/HTMLModule/SafeScripting.php';
|
||||
require 'HTMLPurifier/HTMLModule/Scripting.php';
|
||||
require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tables.php';
|
||||
require 'HTMLPurifier/HTMLModule/Target.php';
|
||||
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
|
||||
require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
|
||||
require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
|
||||
require 'HTMLPurifier/HTMLModule/Text.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy.php';
|
||||
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy/Name.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
|
||||
require 'HTMLPurifier/Injector/AutoParagraph.php';
|
||||
require 'HTMLPurifier/Injector/DisplayLinkURI.php';
|
||||
require 'HTMLPurifier/Injector/Linkify.php';
|
||||
require 'HTMLPurifier/Injector/PurifierLinkify.php';
|
||||
require 'HTMLPurifier/Injector/RemoveEmpty.php';
|
||||
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
|
||||
require 'HTMLPurifier/Injector/SafeObject.php';
|
||||
require 'HTMLPurifier/Lexer/DOMLex.php';
|
||||
require 'HTMLPurifier/Lexer/DirectLex.php';
|
||||
require 'HTMLPurifier/Node/Comment.php';
|
||||
require 'HTMLPurifier/Node/Element.php';
|
||||
require 'HTMLPurifier/Node/Text.php';
|
||||
require 'HTMLPurifier/Strategy/Composite.php';
|
||||
require 'HTMLPurifier/Strategy/Core.php';
|
||||
require 'HTMLPurifier/Strategy/FixNesting.php';
|
||||
require 'HTMLPurifier/Strategy/MakeWellFormed.php';
|
||||
require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
|
||||
require 'HTMLPurifier/Strategy/ValidateAttributes.php';
|
||||
require 'HTMLPurifier/TagTransform/Font.php';
|
||||
require 'HTMLPurifier/TagTransform/Simple.php';
|
||||
require 'HTMLPurifier/Token/Comment.php';
|
||||
require 'HTMLPurifier/Token/Tag.php';
|
||||
require 'HTMLPurifier/Token/Empty.php';
|
||||
require 'HTMLPurifier/Token/End.php';
|
||||
require 'HTMLPurifier/Token/Start.php';
|
||||
require 'HTMLPurifier/Token/Text.php';
|
||||
require 'HTMLPurifier/URIFilter/DisableExternal.php';
|
||||
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
|
||||
require 'HTMLPurifier/URIFilter/DisableResources.php';
|
||||
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
|
||||
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
|
||||
require 'HTMLPurifier/URIFilter/Munge.php';
|
||||
require 'HTMLPurifier/URIFilter/SafeIframe.php';
|
||||
require 'HTMLPurifier/URIScheme/data.php';
|
||||
require 'HTMLPurifier/URIScheme/file.php';
|
||||
require 'HTMLPurifier/URIScheme/ftp.php';
|
||||
require 'HTMLPurifier/URIScheme/http.php';
|
||||
require 'HTMLPurifier/URIScheme/https.php';
|
||||
require 'HTMLPurifier/URIScheme/mailto.php';
|
||||
require 'HTMLPurifier/URIScheme/news.php';
|
||||
require 'HTMLPurifier/URIScheme/nntp.php';
|
||||
require 'HTMLPurifier/URIScheme/tel.php';
|
||||
require 'HTMLPurifier/VarParser/Flexible.php';
|
||||
require 'HTMLPurifier/VarParser/Native.php';
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Emulation layer for code that used kses(), substituting in HTML Purifier.
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';
|
||||
|
||||
function kses($string, $allowed_html, $allowed_protocols = null)
|
||||
{
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$allowed_elements = array();
|
||||
$allowed_attributes = array();
|
||||
foreach ($allowed_html as $element => $attributes) {
|
||||
$allowed_elements[$element] = true;
|
||||
foreach ($attributes as $attribute => $x) {
|
||||
$allowed_attributes["$element.$attribute"] = true;
|
||||
}
|
||||
}
|
||||
$config->set('HTML.AllowedElements', $allowed_elements);
|
||||
$config->set('HTML.AllowedAttributes', $allowed_attributes);
|
||||
if ($allowed_protocols !== null) {
|
||||
$config->set('URI.AllowedSchemes', $allowed_protocols);
|
||||
}
|
||||
$purifier = new HTMLPurifier($config);
|
||||
return $purifier->purify($string);
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Convenience stub file that adds HTML Purifier's library file to the path
|
||||
* without any other side-effects.
|
||||
*/
|
||||
|
||||
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,297 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*! @mainpage
|
||||
*
|
||||
* HTML Purifier is an HTML filter that will take an arbitrary snippet of
|
||||
* HTML and rigorously test, validate and filter it into a version that
|
||||
* is safe for output onto webpages. It achieves this by:
|
||||
*
|
||||
* -# Lexing (parsing into tokens) the document,
|
||||
* -# Executing various strategies on the tokens:
|
||||
* -# Removing all elements not in the whitelist,
|
||||
* -# Making the tokens well-formed,
|
||||
* -# Fixing the nesting of the nodes, and
|
||||
* -# Validating attributes of the nodes; and
|
||||
* -# Generating HTML from the purified tokens.
|
||||
*
|
||||
* However, most users will only need to interface with the HTMLPurifier
|
||||
* and HTMLPurifier_Config.
|
||||
*/
|
||||
|
||||
/*
|
||||
HTML Purifier 4.13.0 - Standards Compliant HTML Filtering
|
||||
Copyright (C) 2006-2008 Edward Z. Yang
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
|
||||
*
|
||||
* @note There are several points in which configuration can be specified
|
||||
* for HTML Purifier. The precedence of these (from lowest to
|
||||
* highest) is as follows:
|
||||
* -# Instance: new HTMLPurifier($config)
|
||||
* -# Invocation: purify($html, $config)
|
||||
* These configurations are entirely independent of each other and
|
||||
* are *not* merged (this behavior may change in the future).
|
||||
*
|
||||
* @todo We need an easier way to inject strategies using the configuration
|
||||
* object.
|
||||
*/
|
||||
class HTMLPurifier
|
||||
{
|
||||
|
||||
/**
|
||||
* Version of HTML Purifier.
|
||||
* @type string
|
||||
*/
|
||||
public $version = '4.13.0';
|
||||
|
||||
/**
|
||||
* Constant with version of HTML Purifier.
|
||||
*/
|
||||
const VERSION = '4.13.0';
|
||||
|
||||
/**
|
||||
* Global configuration object.
|
||||
* @type HTMLPurifier_Config
|
||||
*/
|
||||
public $config;
|
||||
|
||||
/**
|
||||
* Array of extra filter objects to run on HTML,
|
||||
* for backwards compatibility.
|
||||
* @type HTMLPurifier_Filter[]
|
||||
*/
|
||||
private $filters = array();
|
||||
|
||||
/**
|
||||
* Single instance of HTML Purifier.
|
||||
* @type HTMLPurifier
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* @type HTMLPurifier_Strategy_Core
|
||||
*/
|
||||
protected $strategy;
|
||||
|
||||
/**
|
||||
* @type HTMLPurifier_Generator
|
||||
*/
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* Resultant context of last run purification.
|
||||
* Is an array of contexts if the last called method was purifyArray().
|
||||
* @type HTMLPurifier_Context
|
||||
*/
|
||||
public $context;
|
||||
|
||||
/**
|
||||
* Initializes the purifier.
|
||||
*
|
||||
* @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
|
||||
* for all instances of the purifier, if omitted, a default
|
||||
* configuration is supplied (which can be overridden on a
|
||||
* per-use basis).
|
||||
* The parameter can also be any type that
|
||||
* HTMLPurifier_Config::create() supports.
|
||||
*/
|
||||
public function __construct($config = null)
|
||||
{
|
||||
$this->config = HTMLPurifier_Config::create($config);
|
||||
$this->strategy = new HTMLPurifier_Strategy_Core();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filter to process the output. First come first serve
|
||||
*
|
||||
* @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object
|
||||
*/
|
||||
public function addFilter($filter)
|
||||
{
|
||||
trigger_error(
|
||||
'HTMLPurifier->addFilter() is deprecated, use configuration directives' .
|
||||
' in the Filter namespace or Filter.Custom',
|
||||
E_USER_WARNING
|
||||
);
|
||||
$this->filters[] = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters an HTML snippet/document to be XSS-free and standards-compliant.
|
||||
*
|
||||
* @param string $html String of HTML to purify
|
||||
* @param HTMLPurifier_Config $config Config object for this operation,
|
||||
* if omitted, defaults to the config object specified during this
|
||||
* object's construction. The parameter can also be any type
|
||||
* that HTMLPurifier_Config::create() supports.
|
||||
*
|
||||
* @return string Purified HTML
|
||||
*/
|
||||
public function purify($html, $config = null)
|
||||
{
|
||||
// :TODO: make the config merge in, instead of replace
|
||||
$config = $config ? HTMLPurifier_Config::create($config) : $this->config;
|
||||
|
||||
// implementation is partially environment dependant, partially
|
||||
// configuration dependant
|
||||
$lexer = HTMLPurifier_Lexer::create($config);
|
||||
|
||||
$context = new HTMLPurifier_Context();
|
||||
|
||||
// setup HTML generator
|
||||
$this->generator = new HTMLPurifier_Generator($config, $context);
|
||||
$context->register('Generator', $this->generator);
|
||||
|
||||
// set up global context variables
|
||||
if ($config->get('Core.CollectErrors')) {
|
||||
// may get moved out if other facilities use it
|
||||
$language_factory = HTMLPurifier_LanguageFactory::instance();
|
||||
$language = $language_factory->create($config, $context);
|
||||
$context->register('Locale', $language);
|
||||
|
||||
$error_collector = new HTMLPurifier_ErrorCollector($context);
|
||||
$context->register('ErrorCollector', $error_collector);
|
||||
}
|
||||
|
||||
// setup id_accumulator context, necessary due to the fact that
|
||||
// AttrValidator can be called from many places
|
||||
$id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
|
||||
$context->register('IDAccumulator', $id_accumulator);
|
||||
|
||||
$html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
|
||||
|
||||
// setup filters
|
||||
$filter_flags = $config->getBatch('Filter');
|
||||
$custom_filters = $filter_flags['Custom'];
|
||||
unset($filter_flags['Custom']);
|
||||
$filters = array();
|
||||
foreach ($filter_flags as $filter => $flag) {
|
||||
if (!$flag) {
|
||||
continue;
|
||||
}
|
||||
if (strpos($filter, '.') !== false) {
|
||||
continue;
|
||||
}
|
||||
$class = "HTMLPurifier_Filter_$filter";
|
||||
$filters[] = new $class;
|
||||
}
|
||||
foreach ($custom_filters as $filter) {
|
||||
// maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
|
||||
$filters[] = $filter;
|
||||
}
|
||||
$filters = array_merge($filters, $this->filters);
|
||||
// maybe prepare(), but later
|
||||
|
||||
for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
|
||||
$html = $filters[$i]->preFilter($html, $config, $context);
|
||||
}
|
||||
|
||||
// purified HTML
|
||||
$html =
|
||||
$this->generator->generateFromTokens(
|
||||
// list of tokens
|
||||
$this->strategy->execute(
|
||||
// list of un-purified tokens
|
||||
$lexer->tokenizeHTML(
|
||||
// un-purified HTML
|
||||
$html,
|
||||
$config,
|
||||
$context
|
||||
),
|
||||
$config,
|
||||
$context
|
||||
)
|
||||
);
|
||||
|
||||
for ($i = $filter_size - 1; $i >= 0; $i--) {
|
||||
$html = $filters[$i]->postFilter($html, $config, $context);
|
||||
}
|
||||
|
||||
$html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
|
||||
$this->context =& $context;
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters an array of HTML snippets
|
||||
*
|
||||
* @param string[] $array_of_html Array of html snippets
|
||||
* @param HTMLPurifier_Config $config Optional config object for this operation.
|
||||
* See HTMLPurifier::purify() for more details.
|
||||
*
|
||||
* @return string[] Array of purified HTML
|
||||
*/
|
||||
public function purifyArray($array_of_html, $config = null)
|
||||
{
|
||||
$context_array = array();
|
||||
$array = array();
|
||||
foreach($array_of_html as $key=>$value){
|
||||
if (is_array($value)) {
|
||||
$array[$key] = $this->purifyArray($value, $config);
|
||||
} else {
|
||||
$array[$key] = $this->purify($value, $config);
|
||||
}
|
||||
$context_array[$key] = $this->context;
|
||||
}
|
||||
$this->context = $context_array;
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton for enforcing just one HTML Purifier in your system
|
||||
*
|
||||
* @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
|
||||
* HTMLPurifier instance to overload singleton with,
|
||||
* or HTMLPurifier_Config instance to configure the
|
||||
* generated version with.
|
||||
*
|
||||
* @return HTMLPurifier
|
||||
*/
|
||||
public static function instance($prototype = null)
|
||||
{
|
||||
if (!self::$instance || $prototype) {
|
||||
if ($prototype instanceof HTMLPurifier) {
|
||||
self::$instance = $prototype;
|
||||
} elseif ($prototype) {
|
||||
self::$instance = new HTMLPurifier($prototype);
|
||||
} else {
|
||||
self::$instance = new HTMLPurifier();
|
||||
}
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton for enforcing just one HTML Purifier in your system
|
||||
*
|
||||
* @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
|
||||
* HTMLPurifier instance to overload singleton with,
|
||||
* or HTMLPurifier_Config instance to configure the
|
||||
* generated version with.
|
||||
*
|
||||
* @return HTMLPurifier
|
||||
* @note Backwards compatibility, see instance()
|
||||
*/
|
||||
public static function getInstance($prototype = null)
|
||||
{
|
||||
return HTMLPurifier::instance($prototype);
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
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