Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df9715e25 | ||
|
|
7794e16d3d | ||
|
|
ca7f5831fa | ||
|
|
a37fc59b93 |
0
.perm_test_apache
Normal file
0
.perm_test_apache
Normal file
0
.perm_test_exec
Normal file
0
.perm_test_exec
Normal file
311
ai/LocalAIApi.php
Normal file
311
ai/LocalAIApi.php
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
<?php
|
||||||
|
// LocalAIApi — proxy client for the Responses API.
|
||||||
|
// Usage:
|
||||||
|
// require_once __DIR__ . '/ai/LocalAIApi.php';
|
||||||
|
// $response = LocalAIApi::createResponse([
|
||||||
|
// 'input' => [
|
||||||
|
// ['role' => 'system', 'content' => 'You are a helpful assistant.'],
|
||||||
|
// ['role' => 'user', 'content' => 'Tell me a bedtime story.'],
|
||||||
|
// ],
|
||||||
|
// ]);
|
||||||
|
// if (!empty($response['success'])) {
|
||||||
|
// $decoded = LocalAIApi::decodeJsonFromResponse($response);
|
||||||
|
// }
|
||||||
|
|
||||||
|
class LocalAIApi
|
||||||
|
{
|
||||||
|
/** @var array<string,mixed>|null */
|
||||||
|
private static ?array $configCache = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature compatible with the OpenAI Responses API.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $params Request body (model, input, text, reasoning, metadata, etc.).
|
||||||
|
* @param array<string,mixed> $options Extra options (timeout, verify_tls, headers, path, project_uuid).
|
||||||
|
* @return array{
|
||||||
|
* success:bool,
|
||||||
|
* status?:int,
|
||||||
|
* data?:mixed,
|
||||||
|
* error?:string,
|
||||||
|
* response?:mixed,
|
||||||
|
* message?:string
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public static function createResponse(array $params, array $options = []): array
|
||||||
|
{
|
||||||
|
$cfg = self::config();
|
||||||
|
$payload = $params;
|
||||||
|
|
||||||
|
if (empty($payload['input']) || !is_array($payload['input'])) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'input_missing',
|
||||||
|
'message' => 'Parameter "input" is required and must be an array.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($payload['model']) || $payload['model'] === '') {
|
||||||
|
$payload['model'] = $cfg['default_model'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::request($options['path'] ?? null, $payload, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snake_case alias for createResponse (matches the provided example).
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $params
|
||||||
|
* @param array<string,mixed> $options
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
public static function create_response(array $params, array $options = []): array
|
||||||
|
{
|
||||||
|
return self::createResponse($params, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a raw request to the AI proxy.
|
||||||
|
*
|
||||||
|
* @param string $path Endpoint (may be an absolute URL).
|
||||||
|
* @param array<string,mixed> $payload JSON payload.
|
||||||
|
* @param array<string,mixed> $options Additional request options.
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
public static function request(?string $path = null, array $payload = [], array $options = []): array
|
||||||
|
{
|
||||||
|
if (!function_exists('curl_init')) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'curl_missing',
|
||||||
|
'message' => 'PHP cURL extension is missing. Install or enable it on the VM.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cfg = self::config();
|
||||||
|
|
||||||
|
$projectUuid = $cfg['project_uuid'];
|
||||||
|
if (empty($projectUuid)) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'project_uuid_missing',
|
||||||
|
'message' => 'PROJECT_UUID is not defined; aborting AI request.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultPath = $cfg['responses_path'] ?? null;
|
||||||
|
$resolvedPath = $path ?? ($options['path'] ?? $defaultPath);
|
||||||
|
if (empty($resolvedPath)) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'project_id_missing',
|
||||||
|
'message' => 'PROJECT_ID is not defined; cannot resolve AI proxy endpoint.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = self::buildUrl($resolvedPath, $cfg['base_url']);
|
||||||
|
$baseTimeout = isset($cfg['timeout']) ? (int) $cfg['timeout'] : 30;
|
||||||
|
$timeout = isset($options['timeout']) ? (int) $options['timeout'] : $baseTimeout;
|
||||||
|
if ($timeout <= 0) {
|
||||||
|
$timeout = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
$baseVerifyTls = array_key_exists('verify_tls', $cfg) ? (bool) $cfg['verify_tls'] : true;
|
||||||
|
$verifyTls = array_key_exists('verify_tls', $options)
|
||||||
|
? (bool) $options['verify_tls']
|
||||||
|
: $baseVerifyTls;
|
||||||
|
|
||||||
|
$projectHeader = $cfg['project_header'];
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Accept: application/json',
|
||||||
|
];
|
||||||
|
$headers[] = $projectHeader . ': ' . $projectUuid;
|
||||||
|
if (!empty($options['headers']) && is_array($options['headers'])) {
|
||||||
|
foreach ($options['headers'] as $header) {
|
||||||
|
if (is_string($header) && $header !== '') {
|
||||||
|
$headers[] = $header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($projectUuid) && !array_key_exists('project_uuid', $payload)) {
|
||||||
|
$payload['project_uuid'] = $projectUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||||
|
if ($body === false) {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'json_encode_failed',
|
||||||
|
'message' => 'Failed to encode request body to JSON.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifyTls);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifyTls ? 2 : 0);
|
||||||
|
curl_setopt($ch, CURLOPT_FAILONERROR, false);
|
||||||
|
|
||||||
|
$responseBody = curl_exec($ch);
|
||||||
|
if ($responseBody === false) {
|
||||||
|
$error = curl_error($ch) ?: 'Unknown cURL error';
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'curl_error',
|
||||||
|
'message' => $error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$decoded = null;
|
||||||
|
if ($responseBody !== '' && $responseBody !== null) {
|
||||||
|
$decoded = json_decode($responseBody, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
$decoded = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($status >= 200 && $status < 300) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'status' => $status,
|
||||||
|
'data' => $decoded ?? $responseBody,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$errorMessage = 'AI proxy request failed';
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$errorMessage = $decoded['error'] ?? $decoded['message'] ?? $errorMessage;
|
||||||
|
} elseif (is_string($responseBody) && $responseBody !== '') {
|
||||||
|
$errorMessage = $responseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'status' => $status,
|
||||||
|
'error' => $errorMessage,
|
||||||
|
'response' => $decoded ?? $responseBody,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract plain text from a Responses API payload.
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $response Result of LocalAIApi::createResponse|request.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function extractText(array $response): string
|
||||||
|
{
|
||||||
|
$payload = $response['data'] ?? $response;
|
||||||
|
if (!is_array($payload)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($payload['output']) && is_array($payload['output'])) {
|
||||||
|
$combined = '';
|
||||||
|
foreach ($payload['output'] as $item) {
|
||||||
|
if (!isset($item['content']) || !is_array($item['content'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($item['content'] as $block) {
|
||||||
|
if (is_array($block) && ($block['type'] ?? '') === 'output_text' && !empty($block['text'])) {
|
||||||
|
$combined .= $block['text'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($combined !== '') {
|
||||||
|
return $combined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($payload['choices'][0]['message']['content'])) {
|
||||||
|
return (string) $payload['choices'][0]['message']['content'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to decode JSON emitted by the model (handles markdown fences).
|
||||||
|
*
|
||||||
|
* @param array<string,mixed> $response
|
||||||
|
* @return array<string,mixed>|null
|
||||||
|
*/
|
||||||
|
public static function decodeJsonFromResponse(array $response): ?array
|
||||||
|
{
|
||||||
|
$text = self::extractText($response);
|
||||||
|
if ($text === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode($text, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stripped = preg_replace('/^```json|```$/m', '', trim($text));
|
||||||
|
if ($stripped !== null && $stripped !== $text) {
|
||||||
|
$decoded = json_decode($stripped, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load configuration from ai/config.php.
|
||||||
|
*
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
private static function config(): array
|
||||||
|
{
|
||||||
|
if (self::$configCache === null) {
|
||||||
|
$configPath = __DIR__ . '/config.php';
|
||||||
|
if (!file_exists($configPath)) {
|
||||||
|
throw new RuntimeException('AI config file not found: ai/config.php');
|
||||||
|
}
|
||||||
|
$cfg = require $configPath;
|
||||||
|
if (!is_array($cfg)) {
|
||||||
|
throw new RuntimeException('Invalid AI config format: expected array');
|
||||||
|
}
|
||||||
|
self::$configCache = $cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$configCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an absolute URL from base_url and a path.
|
||||||
|
*/
|
||||||
|
private static function buildUrl(string $path, string $baseUrl): string
|
||||||
|
{
|
||||||
|
$trimmed = trim($path);
|
||||||
|
if ($trimmed === '') {
|
||||||
|
return $baseUrl;
|
||||||
|
}
|
||||||
|
if (str_starts_with($trimmed, 'http://') || str_starts_with($trimmed, 'https://')) {
|
||||||
|
return $trimmed;
|
||||||
|
}
|
||||||
|
if ($trimmed[0] === '/') {
|
||||||
|
return $baseUrl . $trimmed;
|
||||||
|
}
|
||||||
|
return $baseUrl . '/' . $trimmed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy alias for backward compatibility with the previous class name.
|
||||||
|
if (!class_exists('OpenAIService')) {
|
||||||
|
class_alias(LocalAIApi::class, 'OpenAIService');
|
||||||
|
}
|
||||||
52
ai/config.php
Normal file
52
ai/config.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
// OpenAI proxy configuration (workspace scope).
|
||||||
|
// Reads values from environment variables or executor/.env.
|
||||||
|
|
||||||
|
$projectUuid = getenv('PROJECT_UUID');
|
||||||
|
$projectId = getenv('PROJECT_ID');
|
||||||
|
|
||||||
|
if (
|
||||||
|
($projectUuid === false || $projectUuid === null || $projectUuid === '') ||
|
||||||
|
($projectId === false || $projectId === null || $projectId === '')
|
||||||
|
) {
|
||||||
|
$envPath = realpath(__DIR__ . '/../../.env'); // executor/.env
|
||||||
|
if ($envPath && is_readable($envPath)) {
|
||||||
|
$lines = @file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if ($line === '' || $line[0] === '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!str_contains($line, '=')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
[$key, $value] = array_map('trim', explode('=', $line, 2));
|
||||||
|
if ($key === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$value = trim($value, "\"' ");
|
||||||
|
if (getenv($key) === false || getenv($key) === '') {
|
||||||
|
putenv("{$key}={$value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$projectUuid = getenv('PROJECT_UUID');
|
||||||
|
$projectId = getenv('PROJECT_ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectUuid = ($projectUuid === false) ? null : $projectUuid;
|
||||||
|
$projectId = ($projectId === false) ? null : $projectId;
|
||||||
|
|
||||||
|
$baseUrl = 'https://flatlogic.com';
|
||||||
|
$responsesPath = $projectId ? "/projects/{$projectId}/ai-request" : null;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'base_url' => $baseUrl,
|
||||||
|
'responses_path' => $responsesPath,
|
||||||
|
'project_id' => $projectId,
|
||||||
|
'project_uuid' => $projectUuid,
|
||||||
|
'project_header' => 'project-uuid',
|
||||||
|
'default_model' => 'gpt-5',
|
||||||
|
'timeout' => 30,
|
||||||
|
'verify_tls' => true,
|
||||||
|
];
|
||||||
32
api/send_message.php
Normal file
32
api/send_message.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Not logged in']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Invalid request method']);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once '../db/config.php';
|
||||||
|
|
||||||
|
$sender_id = $_SESSION['user_id'];
|
||||||
|
$receiver_id = isset($_POST['receiver_id']) ? (int)$_POST['receiver_id'] : 0;
|
||||||
|
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
|
||||||
|
|
||||||
|
if ($receiver_id && !empty($message)) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO messages (sender_id, receiver_id, message) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute([$sender_id, $receiver_id, $message]);
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Invalid input']);
|
||||||
|
}
|
||||||
3
assets/css/custom.css
Normal file
3
assets/css/custom.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
5
composer.json
Normal file
5
composer.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"cboden/ratchet": "^0.4.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
1305
composer.lock
generated
Normal file
1305
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
150
dashboard.php
Normal file
150
dashboard.php
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
include 'header.php';
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$current_user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Check if we are viewing a specific chat
|
||||||
|
$chat_with_id = isset($_GET['chat_with_id']) ? (int)$_GET['chat_with_id'] : null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ($chat_with_id) {
|
||||||
|
// Chat view
|
||||||
|
$stmt = $pdo->prepare('SELECT display_name FROM users WHERE id = ?');
|
||||||
|
$stmt->execute([$chat_with_id]);
|
||||||
|
$chat_with_user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Fetch messages
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM messages WHERE (sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?) ORDER BY created_at ASC');
|
||||||
|
$stmt->execute([$current_user_id, $chat_with_id, $chat_with_id, $current_user_id]);
|
||||||
|
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="d-flex flex-column" style="height: calc(100vh - 56px);">
|
||||||
|
<header class="p-3 bg-light border-bottom">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<a href="dashboard.php" class="btn btn-secondary">< Back</a>
|
||||||
|
<h1 class="h5 mb-0">Chat with <?php echo htmlspecialchars($chat_with_user['display_name']); ?></h1>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="flex-grow-1" style="overflow-y: auto;">
|
||||||
|
<div class="container-fluid p-3">
|
||||||
|
<div class="d-flex flex-column gap-3" id="chat-messages">
|
||||||
|
<?php foreach ($messages as $message): ?>
|
||||||
|
<div class="p-2 rounded <?php echo $message['sender_id'] == $current_user_id ? 'bg-light text-dark align-self-end text-end' : 'bg-primary text-white align-self-start'; ?> col-8">
|
||||||
|
<?php echo htmlspecialchars($message['message']); ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="p-3 bg-light border-top">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<form id="message-form" onsubmit="sendMessage(event)">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="hidden" name="receiver_id" value="<?php echo $chat_with_id; ?>">
|
||||||
|
<input type="text" name="message" class="form-control" placeholder="Type a message..." required>
|
||||||
|
<button class="btn btn-primary" type="submit" id="send-button">Send</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
} else {
|
||||||
|
// Contact list view
|
||||||
|
$stmt = $pdo->prepare('SELECT id, display_name FROM users WHERE id != ?');
|
||||||
|
$stmt->execute([$current_user_id]);
|
||||||
|
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1 class="h3 mb-3">Contacts</h1>
|
||||||
|
<div class="list-group">
|
||||||
|
<?php foreach ($users as $user): ?>
|
||||||
|
<a href="dashboard.php?chat_with_id=<?php echo $user['id']; ?>" class="list-group-item list-group-item-action">
|
||||||
|
<?php echo htmlspecialchars($user['display_name']); ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
if (document.getElementById('message-form')) {
|
||||||
|
const chatMessages = document.getElementById('chat-messages');
|
||||||
|
const messageForm = document.getElementById('message-form');
|
||||||
|
const messageInput = messageForm.querySelector('input[name="message"]');
|
||||||
|
const receiverId = messageForm.querySelector('input[name="receiver_id"]').value;
|
||||||
|
const senderId = <?php echo $current_user_id; ?>;
|
||||||
|
|
||||||
|
const conn = new WebSocket(`ws://${window.location.hostname}:8080?user_id=${senderId}`);
|
||||||
|
|
||||||
|
conn.onopen = function(e) {
|
||||||
|
console.log("Connection established!");
|
||||||
|
};
|
||||||
|
|
||||||
|
conn.onmessage = function(e) {
|
||||||
|
const msg = JSON.parse(e.data);
|
||||||
|
|
||||||
|
// Only append if the message is part of the current chat
|
||||||
|
if ((msg.sender_id == senderId && msg.receiver_id == receiverId) || (msg.sender_id == receiverId && msg.receiver_id == senderId)) {
|
||||||
|
const messageElement = document.createElement('div');
|
||||||
|
messageElement.classList.add('p-2', 'rounded', 'col-8');
|
||||||
|
|
||||||
|
if (msg.sender_id == senderId) {
|
||||||
|
messageElement.classList.add('bg-light', 'text-dark', 'align-self-end', 'text-end');
|
||||||
|
} else {
|
||||||
|
messageElement.classList.add('bg-primary', 'text-white', 'align-self-start');
|
||||||
|
}
|
||||||
|
|
||||||
|
messageElement.innerHTML = msg.message;
|
||||||
|
chatMessages.appendChild(messageElement);
|
||||||
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
conn.onclose = function(e) {
|
||||||
|
console.log("Connection closed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMessage(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const message = messageInput.value;
|
||||||
|
if (message) {
|
||||||
|
const data = {
|
||||||
|
sender_id: senderId,
|
||||||
|
receiver_id: receiverId,
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
conn.send(JSON.stringify(data));
|
||||||
|
|
||||||
|
// Append sent message immediately
|
||||||
|
const messageElement = document.createElement('div');
|
||||||
|
messageElement.classList.add('p-2', 'rounded', 'col-8', 'bg-light', 'text-dark', 'align-self-end', 'text-end');
|
||||||
|
messageElement.innerHTML = message;
|
||||||
|
chatMessages.appendChild(messageElement);
|
||||||
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||||
|
|
||||||
|
messageInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
||||||
21
db/migrations/001_create_messages_table.php
Normal file
21
db/migrations/001_create_messages_table.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "
|
||||||
|
CREATE TABLE IF NOT EXISTS messages (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
sender_id INT NOT NULL,
|
||||||
|
receiver_id INT NOT NULL,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (receiver_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
);";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'messages' created successfully (if it didn't exist).\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("DB MIGRATION ERROR: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
59
db/setup.php
Normal file
59
db/setup.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Connect without a database selected
|
||||||
|
$pdo_admin = new PDO('mysql:host='.DB_HOST.';charset=utf8mb4', DB_USER, DB_PASS, [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 2. Create the database if it doesn't exist
|
||||||
|
$pdo_admin->exec("CREATE DATABASE IF NOT EXISTS ".DB_NAME);
|
||||||
|
echo "Database '" . DB_NAME . "' created or already exists.\n";
|
||||||
|
|
||||||
|
// 3. Now, connect to the specific database and create the table
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
display_name VARCHAR(50) NOT NULL,
|
||||||
|
email VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);";
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Table 'users' created successfully (if it didn\'t exist).\n";
|
||||||
|
|
||||||
|
// 4. Run migrations
|
||||||
|
$migration_files = glob(__DIR__ . '/migrations/*.php');
|
||||||
|
foreach ($migration_files as $file) {
|
||||||
|
require_once $file;
|
||||||
|
echo "Ran migration: $file\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Seed the users table if it's empty
|
||||||
|
$stmt = $pdo->query("SELECT COUNT(*) FROM users");
|
||||||
|
if ($stmt->fetchColumn() == 0) {
|
||||||
|
$users = [
|
||||||
|
['display_name' => 'John Doe', 'email' => 'john.doe@example.com', 'password' => 'password123'],
|
||||||
|
['display_name' => 'Jane Smith', 'email' => 'jane.smith@example.com', 'password' => 'password123'],
|
||||||
|
['display_name' => 'Peter Jones', 'email' => 'peter.jones@example.com', 'password' => 'password123'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$sql = "INSERT INTO users (display_name, email, password_hash) VALUES (:display_name, :email, :password_hash)";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$stmt->execute([
|
||||||
|
':display_name' => $user['display_name'],
|
||||||
|
':email' => $user['email'],
|
||||||
|
':password_hash' => password_hash($user['password'], PASSWORD_DEFAULT)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
echo "Seeded the users table with 3 sample users.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("DB SETUP ERROR: " . $e->getMessage());
|
||||||
|
}
|
||||||
6
footer.php
Normal file
6
footer.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
76
header.php
Normal file
76
header.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php session_start(); ?>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>harmony3</title>
|
||||||
|
<meta name="description" content="A simple user-to-user messaging system.">
|
||||||
|
<meta name="keywords" content="messaging, chat, private messages, user-to-user, real-time chat, harmony3, Built with Flatlogic Generator">
|
||||||
|
<meta property="og:title" content="harmony3">
|
||||||
|
<meta property="og:description" content="A simple user-to-user messaging system.">
|
||||||
|
<meta property="og:image" content="">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:image" content="">
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<link href="assets/css/custom.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="d-flex flex-column min-vh-100">
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasExample" aria-controls="offcanvasExample">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand" href="index.php">
|
||||||
|
<i class="bi bi-chat-quote-fill"></i>
|
||||||
|
harmony3
|
||||||
|
</a>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="dashboard.php">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="logout.php">Logout</a>
|
||||||
|
</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="login.php">Sign In</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link btn btn-primary btn-sm px-3" href="signup.php">Sign Up</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasExample" aria-labelledby="offcanvasExampleLabel">
|
||||||
|
<div class="offcanvas-header">
|
||||||
|
<h5 class="offcanvas-title" id="offcanvasExampleLabel">Chats</h5>
|
||||||
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body">
|
||||||
|
<div>
|
||||||
|
Select a user to start a conversation.
|
||||||
|
</div>
|
||||||
|
<div class="dropdown mt-3">
|
||||||
|
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown">
|
||||||
|
Select User
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||||
|
<li><a class="dropdown-item" href="#">User 1</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#">User 2</a></li>
|
||||||
|
<li><a class="dropdown-item" href="#">User 3</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
187
index.php
187
index.php
@ -1,150 +1,43 @@
|
|||||||
<?php
|
<?php include 'header.php'; ?>
|
||||||
declare(strict_types=1);
|
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
<main class="container my-5">
|
||||||
$now = date('Y-m-d H:i:s');
|
<div class="row align-items-center">
|
||||||
?>
|
<div class="col-md-6">
|
||||||
<!doctype html>
|
<h1 class="display-4 fw-bold">Connect Privately.</h1>
|
||||||
<html lang="en">
|
<p class="lead text-muted">A simple, secure, and real-time messaging platform for personal conversations. Your space to talk, without the noise.</p>
|
||||||
<head>
|
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
|
||||||
<meta charset="utf-8" />
|
<a href="signup.php" class="btn btn-primary btn-lg px-4 me-md-2">Get Started</a>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<a href="login.php" class="btn btn-outline-secondary btn-lg px-4">Sign In</a>
|
||||||
<title>New Style</title>
|
|
||||||
<?php
|
|
||||||
// Read project preview data from environment
|
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($projectImageUrl): ?>
|
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<!-- Twitter image -->
|
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% { background-position: 0% 0%; }
|
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<div class="card">
|
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
|
||||||
<span class="sr-only">Loading…</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
<div class="col-md-6 text-center">
|
||||||
<footer>
|
<i class="bi bi-shield-lock-fill" style="font-size: 12rem; color: #0D6EFD;"></i>
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
</div>
|
||||||
</footer>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
<div class="row text-center g-4 py-5 mt-5">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="feature-icon mb-3">
|
||||||
|
<i class="bi bi-person-badge"></i>
|
||||||
|
</div>
|
||||||
|
<h2 class="fw-normal">User Profiles</h2>
|
||||||
|
<p>Create your profile, set your display name, and connect with others.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="feature-icon mb-3">
|
||||||
|
<i class="bi bi-chat-dots"></i>
|
||||||
|
</div>
|
||||||
|
<h2 class="fw-normal">Direct Messaging</h2>
|
||||||
|
<p>Start one-on-one conversations with any other registered user on the platform.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="feature-icon mb-3">
|
||||||
|
<i class="bi bi-broadcast"></i>
|
||||||
|
</div>
|
||||||
|
<h2 class="fw-normal">Real-Time Chat</h2>
|
||||||
|
<p>Experience live messaging with WebSocket technology for instant communication.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php include 'footer.php'; ?>
|
||||||
106
login.php
Normal file
106
login.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
// Start session and load DB config right away.
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
|
||||||
|
// --- STANDARD PAGE LOAD (NON-AJAX) ---
|
||||||
|
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
// Include header AFTER ajax block.
|
||||||
|
include 'header.php';
|
||||||
|
|
||||||
|
// If user is already logged in, redirect to dashboard
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: dashboard.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
$registered_success = isset($_GET['registered']);
|
||||||
|
|
||||||
|
// This block now only handles the STANDARD (non-fetch) form submission.
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
echo '<div class="alert alert-info">DEBUG: Standard POST handler reached.</div>';
|
||||||
|
echo '<pre class="alert alert-warning">DEBUG: $_POST data: ' . htmlspecialchars(print_r($_POST, true)) . '</pre>';
|
||||||
|
|
||||||
|
$email = trim($_POST['login_email']);
|
||||||
|
$password = $_POST['login_pass'];
|
||||||
|
|
||||||
|
if (empty($email)) {
|
||||||
|
$errors[] = 'Email is required.';
|
||||||
|
}
|
||||||
|
if (empty($password)) {
|
||||||
|
$errors[] = 'Password is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT id, display_name, password_hash FROM users WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password_hash'])) {
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['display_name'] = $user['display_name'];
|
||||||
|
// Standard PHP redirect
|
||||||
|
header("Location: dashboard.php");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$errors[] = 'Invalid email or password.';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("Database error in login.php (Standard): " . $e->getMessage());
|
||||||
|
$errors[] = 'A server error occurred. Please try again later.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<main class="container flex-grow-1 d-flex align-items-center justify-content-center">
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="card my-5">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h1 class="card-title text-center mb-4">Sign In</h1>
|
||||||
|
|
||||||
|
<div id="alert-container">
|
||||||
|
<?php if ($registered_success): ?>
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
Registration successful! You can now sign in.
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($errors)): ?>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<?php foreach ($errors as $error): ?>
|
||||||
|
<p class="mb-0"><?php echo htmlspecialchars($error); ?></p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="login-form" method="POST" action="login.php">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email address</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="login_email" placeholder="name@example.com" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="login_pass" required>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary">Sign In</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center py-3">
|
||||||
|
<small class="text-muted">Don't have an account? <a href="signup.php">Sign Up</a></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php include 'footer.php'; ?>
|
||||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
96
signup.php
Normal file
96
signup.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
$success_message = '';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$display_name = trim($_POST['display_name']);
|
||||||
|
$email = trim($_POST['email']);
|
||||||
|
$password = $_POST['password'];
|
||||||
|
|
||||||
|
if (empty($display_name)) {
|
||||||
|
$errors[] = 'Display name is required.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($email)) {
|
||||||
|
$errors[] = 'Email is required.';
|
||||||
|
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errors[] = 'Invalid email format.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($password)) {
|
||||||
|
$errors[] = 'Password is required.';
|
||||||
|
} elseif (strlen($password) < 8) {
|
||||||
|
$errors[] = 'Password must be at least 8 characters long.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$errors[] = 'Email address is already registered.';
|
||||||
|
} else {
|
||||||
|
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO users (display_name, email, password_hash) VALUES (?, ?, ?)");
|
||||||
|
if ($stmt->execute([$display_name, $email, $hashed_password])) {
|
||||||
|
// Redirect to login page after successful registration
|
||||||
|
header("Location: login.php?registered=true");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$errors[] = 'Failed to create account. Please try again later.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log($e->getMessage());
|
||||||
|
$errors[] = 'Database error. Please try again later.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main class="container flex-grow-1 d-flex align-items-center justify-content-center">
|
||||||
|
<div class="col-md-6 col-lg-4">
|
||||||
|
<div class="card my-5">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h1 class="card-title text-center mb-4">Create Account</h1>
|
||||||
|
|
||||||
|
<?php if (!empty($errors)): ?>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<?php foreach ($errors as $error): ?>
|
||||||
|
<p class="mb-0"><?php echo htmlspecialchars($error); ?></p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form action="signup.php" method="post" novalidate>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="displayName" class="form-label">Display Name</label>
|
||||||
|
<input type="text" class="form-control" id="displayName" name="display_name" required value="<?php echo isset($_POST['display_name']) ? htmlspecialchars($_POST['display_name']) : ''; ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email address</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" placeholder="name@example.com" required value="<?php echo isset($_POST['email']) ? htmlspecialchars($_POST['email']) : ''; ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary">Sign Up</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center py-3">
|
||||||
|
<small class="text-muted">Already have an account? <a href="login.php">Sign In</a></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php include 'footer.php'; ?>
|
||||||
22
vendor/autoload.php
vendored
Normal file
22
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID < 50600) {
|
||||||
|
if (!headers_sent()) {
|
||||||
|
header('HTTP/1.1 500 Internal Server Error');
|
||||||
|
}
|
||||||
|
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||||
|
if (!ini_get('display_errors')) {
|
||||||
|
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||||
|
fwrite(STDERR, $err);
|
||||||
|
} elseif (!headers_sent()) {
|
||||||
|
echo $err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException($err);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInitce559b9fbd0e097bccee77e6c3cc8be1::getLoader();
|
||||||
52
vendor/cboden/ratchet/.github/workflows/ci.yml
vendored
Normal file
52
vendor/cboden/ratchet/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: "CI"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
schedule:
|
||||||
|
- cron: "42 3 * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
phpunit:
|
||||||
|
name: "PHPUnit"
|
||||||
|
runs-on: "ubuntu-20.04"
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-version:
|
||||||
|
- "5.4"
|
||||||
|
- "5.5"
|
||||||
|
- "5.6"
|
||||||
|
- "7.0"
|
||||||
|
- "7.1"
|
||||||
|
- "7.2"
|
||||||
|
- "7.3"
|
||||||
|
- "7.4"
|
||||||
|
dependencies:
|
||||||
|
- "highest"
|
||||||
|
include:
|
||||||
|
- dependencies: "lowest"
|
||||||
|
php-version: "5.4"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v2"
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-version }}"
|
||||||
|
coverage: "none"
|
||||||
|
ini-values: "zend.assertions=1"
|
||||||
|
|
||||||
|
- name: "Install dependencies with Composer"
|
||||||
|
uses: "ramsey/composer-install@v1"
|
||||||
|
with:
|
||||||
|
dependency-versions: "${{ matrix.dependencies }}"
|
||||||
|
|
||||||
|
- name: "Run PHPUnit"
|
||||||
|
run: "vendor/bin/phpunit"
|
||||||
5
vendor/cboden/ratchet/.gitignore
vendored
Normal file
5
vendor/cboden/ratchet/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
phpunit.xml
|
||||||
|
reports
|
||||||
|
sandbox
|
||||||
|
vendor
|
||||||
|
composer.lock
|
||||||
150
vendor/cboden/ratchet/CHANGELOG.md
vendored
Normal file
150
vendor/cboden/ratchet/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
### Legend
|
||||||
|
|
||||||
|
* "BC": Backwards compatibility break (from public component APIs)
|
||||||
|
* "BF": Bug fix
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* 0.4.4 (2021-12-11)
|
||||||
|
* Correct and update dependencies for forward compatibility
|
||||||
|
* Added context for React Socket server to App
|
||||||
|
* Use non-deprecated Guzzle API calls
|
||||||
|
|
||||||
|
* 0.4.3 (2020-06-04)
|
||||||
|
* BF: Fixed interface acceptable regression in `App`
|
||||||
|
* Update RFC6455 library with latest fixes
|
||||||
|
|
||||||
|
* 0.4.2 (2020-01-27)
|
||||||
|
* Support Symfony 5
|
||||||
|
* BF: Use phpunit from vendor directory
|
||||||
|
* Allow disabling of xdebug warning by defining `RATCHET_DISABLE_XDEBUG_WARN`
|
||||||
|
* Stop using `LoopInterface::tick()` for testing
|
||||||
|
|
||||||
|
* 0.4.1 (2017-12-11)
|
||||||
|
* Only enableKeepAlive in App if no WsServer passed allowing user to set their own timeout duration
|
||||||
|
* Support Symfony 4
|
||||||
|
* BF: Plug NOOP controller in connection from router in case of misbehaving client
|
||||||
|
* BF: Raise error from invalid WAMP payload
|
||||||
|
|
||||||
|
* 0.4 (2017-09-14)
|
||||||
|
* BC: $conn->WebSocket->request replaced with $conn->httpRequest which is a PSR-7 object
|
||||||
|
* Binary messages now supported via Ratchet\WebSocket\MessageComponentInterface
|
||||||
|
* Added heartbeat support via ping/pong in WsServer
|
||||||
|
* BC: No longer support old (and insecure) Hixie76 and Hybi protocols
|
||||||
|
* BC: No longer support disabling UTF-8 checks
|
||||||
|
* BC: The Session component implements HttpServerInterface instead of WsServerInterface
|
||||||
|
* BC: PHP 5.3 no longer supported
|
||||||
|
* BC: Update to newer version of react/socket dependency
|
||||||
|
* BC: WAMP topics reduced to 0 subscriptions are deleted, new subs to same name will result in new Topic instance
|
||||||
|
* Significant performance enhancements
|
||||||
|
|
||||||
|
* 0.3.6 (2017-01-06)
|
||||||
|
* BF: Keep host and scheme in HTTP request object attatched to connection
|
||||||
|
* BF: Return correct HTTP response (405) when non-GET request made
|
||||||
|
|
||||||
|
* 0.3.5 (2016-05-25)
|
||||||
|
* BF: Unmask responding close frame
|
||||||
|
* Added write handler for PHP session serializer
|
||||||
|
|
||||||
|
* 0.3.4 (2015-12-23)
|
||||||
|
* BF: Edge case where version check wasn't run on message coalesce
|
||||||
|
* BF: Session didn't start when using pdo_sqlite
|
||||||
|
* BF: WAMP currie prefix check when using '#'
|
||||||
|
* Compatibility with Symfony 3
|
||||||
|
|
||||||
|
* 0.3.3 (2015-05-26)
|
||||||
|
* BF: Framing bug on large messages upon TCP fragmentation
|
||||||
|
* BF: Symfony Router query parameter defaults applied to Request
|
||||||
|
* BF: WAMP CURIE on all URIs
|
||||||
|
* OriginCheck rules applied to FlashPolicy
|
||||||
|
* Switched from PSR-0 to PSR-4
|
||||||
|
|
||||||
|
* 0.3.2 (2014-06-08)
|
||||||
|
* BF: No messages after closing handshake (fixed rare race condition causing 100% CPU)
|
||||||
|
* BF: Fixed accidental BC break from v0.3.1
|
||||||
|
* Added autoDelete parameter to Topic to destroy when empty of connections
|
||||||
|
* Exposed React Socket on IoServer (allowing FlashPolicy shutdown in App)
|
||||||
|
* Normalized Exceptions in WAMP
|
||||||
|
|
||||||
|
* 0.3.1 (2014-05-26)
|
||||||
|
* Added query parameter support to Router, set in HTTP request (ws://server?hello=world)
|
||||||
|
* HHVM compatibility
|
||||||
|
* BF: React/0.4 support; CPU starvation bug fixes
|
||||||
|
* BF: Allow App::route to ignore Host header
|
||||||
|
* Added expected filters to WAMP Topic broadcast method
|
||||||
|
* Resource cleanup in WAMP TopicManager
|
||||||
|
|
||||||
|
* 0.3.0 (2013-10-14)
|
||||||
|
* Added the `App` class to help making Ratchet so easy to use it's silly
|
||||||
|
* BC: Require hostname to do HTTP Host header match and do Origin HTTP header check, verify same name by default, helping prevent CSRF attacks
|
||||||
|
* Added Symfony/2.2 based HTTP Router component to allowing for a single Ratchet server to handle multiple apps -> Ratchet\Http\Router
|
||||||
|
* BC: Decoupled HTTP from WebSocket component -> Ratchet\Http\HttpServer
|
||||||
|
* BF: Single sub-protocol selection to conform with RFC6455
|
||||||
|
* BF: Sanity checks on WAMP protocol to prevent errors
|
||||||
|
|
||||||
|
* 0.2.8 (2013-09-19)
|
||||||
|
* React 0.3 support
|
||||||
|
|
||||||
|
* 0.2.7 (2013-06-09)
|
||||||
|
* BF: Sub-protocol negotation with Guzzle 3.6
|
||||||
|
|
||||||
|
* 0.2.6 (2013-06-01)
|
||||||
|
* Guzzle 3.6 support
|
||||||
|
|
||||||
|
* 0.2.5 (2013-04-01)
|
||||||
|
* Fixed Hixie-76 handshake bug
|
||||||
|
|
||||||
|
* 0.2.4 (2013-03-09)
|
||||||
|
* Support for Symfony 2.2 and Guzzle 2.3
|
||||||
|
* Minor bug fixes when handling errors
|
||||||
|
|
||||||
|
* 0.2.3 (2012-11-21)
|
||||||
|
* Bumped dep: Guzzle to v3, React to v0.2.4
|
||||||
|
* More tests
|
||||||
|
|
||||||
|
* 0.2.2 (2012-10-20)
|
||||||
|
* Bumped deps to use React v0.2
|
||||||
|
|
||||||
|
* 0.2.1 (2012-10-13)
|
||||||
|
* BF: No more UTF-8 warnings in browsers (no longer sending empty sub-protocol string)
|
||||||
|
* Documentation corrections
|
||||||
|
* Using new composer structure
|
||||||
|
|
||||||
|
* 0.2 (2012-09-07)
|
||||||
|
* Ratchet passes every non-binary-frame test from the Autobahn Testsuite
|
||||||
|
* Major performance improvements
|
||||||
|
* BC: Renamed "WampServer" to "ServerProtocol"
|
||||||
|
* BC: New "WampServer" component passes Topic container objects of subscribed Connections
|
||||||
|
* Option to turn off UTF-8 checks in order to increase performance
|
||||||
|
* Switched dependency guzzle/guzzle to guzzle/http (no API changes)
|
||||||
|
* mbstring no longer required
|
||||||
|
|
||||||
|
* 0.1.5 (2012-07-12)
|
||||||
|
* BF: Error where service wouldn't run on PHP <= 5.3.8
|
||||||
|
* Dependency library updates
|
||||||
|
|
||||||
|
* 0.1.4 (2012-06-17)
|
||||||
|
* Fixed dozens of failing AB tests
|
||||||
|
* BF: Proper socket buffer handling
|
||||||
|
|
||||||
|
* 0.1.3 (2012-06-15)
|
||||||
|
* Major refactor inside WebSocket protocol handling, more loosley coupled
|
||||||
|
* BF: Proper error handling on failed WebSocket connections
|
||||||
|
* BF: Handle TCP message concatenation
|
||||||
|
* Inclusion of the AutobahnTestSuite checking WebSocket protocol compliance
|
||||||
|
* mb_string now a requirement
|
||||||
|
|
||||||
|
* 0.1.2 (2012-05-19)
|
||||||
|
* BC/BF: Updated WAMP API to coincide with the official spec
|
||||||
|
* Tweaks to improve running as a long lived process
|
||||||
|
|
||||||
|
* 0.1.1 (2012-05-14)
|
||||||
|
* Separated interfaces allowing WebSockets to support multiple sub protocols
|
||||||
|
* BF: remoteAddress variable on connections returns proper value
|
||||||
|
|
||||||
|
* 0.1 (2012-05-11)
|
||||||
|
* First release with components: IoServer, WsServer, SessionProvider, WampServer, FlashPolicy, IpBlackList
|
||||||
|
* I/O now handled by React, making Ratchet fully asynchronous
|
||||||
19
vendor/cboden/ratchet/LICENSE
vendored
Normal file
19
vendor/cboden/ratchet/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2011 Chris Boden
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
42
vendor/cboden/ratchet/Makefile
vendored
Normal file
42
vendor/cboden/ratchet/Makefile
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# This file is intended to ease the author's development and testing process
|
||||||
|
# Users do not need to use `make`; Ratchet does not need to be compiled
|
||||||
|
|
||||||
|
test:
|
||||||
|
vendor/bin/phpunit
|
||||||
|
|
||||||
|
cover:
|
||||||
|
vendor/bin/phpunit --coverage-text --coverage-html=reports/coverage
|
||||||
|
|
||||||
|
abtests:
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8001 LibEvent &
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8002 StreamSelect &
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8004 LibEv &
|
||||||
|
wstest -m testeeserver -w ws://localhost:8000 &
|
||||||
|
sleep 1
|
||||||
|
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-all.json
|
||||||
|
killall php wstest
|
||||||
|
|
||||||
|
abtest:
|
||||||
|
ulimit -n 2048 && php tests/autobahn/bin/fuzzingserver.php 8000 StreamSelect &
|
||||||
|
sleep 1
|
||||||
|
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-quick.json
|
||||||
|
killall php
|
||||||
|
|
||||||
|
profile:
|
||||||
|
php -d 'xdebug.profiler_enable=1' tests/autobahn/bin/fuzzingserver.php 8000 LibEvent &
|
||||||
|
sleep 1
|
||||||
|
wstest -m fuzzingclient -s tests/autobahn/fuzzingclient-profile.json
|
||||||
|
killall php
|
||||||
|
|
||||||
|
apidocs:
|
||||||
|
apigen --title Ratchet -d reports/api \
|
||||||
|
-s src/ \
|
||||||
|
-s vendor/ratchet/rfc6455/src \
|
||||||
|
-s vendor/react/event-loop/src \
|
||||||
|
-s vendor/react/socket/src \
|
||||||
|
-s vendor/react/stream/src \
|
||||||
|
-s vendor/psr/http-message/src \
|
||||||
|
-s vendor/symfony/http-foundation/Session \
|
||||||
|
-s vendor/symfony/routing \
|
||||||
|
-s vendor/evenement/evenement/src/Evenement \
|
||||||
|
--exclude=vendor/symfony/routing/Tests \
|
||||||
87
vendor/cboden/ratchet/README.md
vendored
Normal file
87
vendor/cboden/ratchet/README.md
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# Ratchet
|
||||||
|
|
||||||
|
[![GitHub Actions][GA Image]][GA Link]
|
||||||
|
[](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!'); };
|
||||||
|
```
|
||||||
|
|
||||||
|
[GA Image]: https://github.com/ratchetphp/Ratchet/workflows/CI/badge.svg
|
||||||
|
|
||||||
|
[GA Link]: https://github.com/ratchetphp/Ratchet/actions?query=workflow%3A%22CI%22+branch%3Amaster
|
||||||
8
vendor/cboden/ratchet/SECURITY.md
vendored
Normal file
8
vendor/cboden/ratchet/SECURITY.md
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Please report security issues to:
|
||||||
|
|
||||||
|
* Chris Boden [cboden@gmail.com](cboden@gmail.com)
|
||||||
|
* Matt Bonneau [matt@bonneau.net](matt@bonneau.net)
|
||||||
40
vendor/cboden/ratchet/composer.json
vendored
Normal file
40
vendor/cboden/ratchet/composer.json
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"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.1"
|
||||||
|
, "react/socket": "^1.0 || ^0.8 || ^0.7 || ^0.6 || ^0.5"
|
||||||
|
, "react/event-loop": ">=0.4"
|
||||||
|
, "guzzlehttp/psr7": "^1.7|^2.0"
|
||||||
|
, "symfony/http-foundation": "^2.6|^3.0|^4.0|^5.0|^6.0"
|
||||||
|
, "symfony/routing": "^2.6|^3.0|^4.0|^5.0|^6.0"
|
||||||
|
}
|
||||||
|
, "require-dev": {
|
||||||
|
"phpunit/phpunit": "~4.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
vendor/cboden/ratchet/phpunit.xml.dist
vendored
Normal file
30
vendor/cboden/ratchet/phpunit.xml.dist
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
forceCoversAnnotation="true"
|
||||||
|
mapTestClassNameToCoveredClassName="true"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
colors="true"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
syntaxCheck="false"
|
||||||
|
stopOnError="false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit">
|
||||||
|
<directory>./tests/unit/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="integration">
|
||||||
|
<directory>./tests/integration/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./src/</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
||||||
41
vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php
vendored
Normal file
41
vendor/cboden/ratchet/src/Ratchet/AbstractConnectionDecorator.php
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps ConnectionInterface objects via the decorator pattern but allows
|
||||||
|
* parameters to bubble through with magic methods
|
||||||
|
* @todo It sure would be nice if I could make most of this a trait...
|
||||||
|
*/
|
||||||
|
abstract class AbstractConnectionDecorator implements ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* @var ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $wrappedConn;
|
||||||
|
|
||||||
|
public function __construct(ConnectionInterface $conn) {
|
||||||
|
$this->wrappedConn = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected function getConnection() {
|
||||||
|
return $this->wrappedConn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($name, $value) {
|
||||||
|
$this->wrappedConn->$name = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($name) {
|
||||||
|
return $this->wrappedConn->$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset($name) {
|
||||||
|
return isset($this->wrappedConn->$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unset($name) {
|
||||||
|
unset($this->wrappedConn->$name);
|
||||||
|
}
|
||||||
|
}
|
||||||
147
vendor/cboden/ratchet/src/Ratchet/App.php
vendored
Normal file
147
vendor/cboden/ratchet/src/Ratchet/App.php
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?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.
|
||||||
|
* @param array $context
|
||||||
|
*/
|
||||||
|
public function __construct($httpHost = 'localhost', $port = 8080, $address = '127.0.0.1', LoopInterface $loop = null, $context = array()) {
|
||||||
|
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, $context);
|
||||||
|
|
||||||
|
$this->routes = new RouteCollection;
|
||||||
|
$this->_server = new IoServer(new HttpServer(new Router(new UrlMatcher($this->routes, new RequestContext))), $socket, $loop);
|
||||||
|
|
||||||
|
$policy = new FlashPolicy;
|
||||||
|
$policy->addAllowedAccess($httpHost, 80);
|
||||||
|
$policy->addAllowedAccess($httpHost, $port);
|
||||||
|
|
||||||
|
if (80 == $port) {
|
||||||
|
$flashUri = '0.0.0.0:843';
|
||||||
|
} else {
|
||||||
|
$flashUri = 8843;
|
||||||
|
}
|
||||||
|
$flashSock = new Reactor($flashUri, $loop);
|
||||||
|
$this->flashServer = new IoServer($policy, $flashSock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an endpoint/application to the server
|
||||||
|
* @param string $path The URI the client will connect to
|
||||||
|
* @param ComponentInterface $controller Your application to server for the route. If not specified, assumed to be for a WebSocket
|
||||||
|
* @param array $allowedOrigins An array of hosts allowed to connect (same host by default), ['*'] for any
|
||||||
|
* @param string $httpHost Override the $httpHost variable provided in the __construct
|
||||||
|
* @return ComponentInterface|WsServer
|
||||||
|
*/
|
||||||
|
public function route($path, ComponentInterface $controller, array $allowedOrigins = array(), $httpHost = null) {
|
||||||
|
if ($controller instanceof HttpServerInterface || $controller instanceof WsServer) {
|
||||||
|
$decorated = $controller;
|
||||||
|
} elseif ($controller instanceof WampServerInterface) {
|
||||||
|
$decorated = new WsServer(new WampServer($controller));
|
||||||
|
$decorated->enableKeepAlive($this->_server->loop);
|
||||||
|
} elseif ($controller instanceof MessageComponentInterface || $controller instanceof WsMessageComponentInterface) {
|
||||||
|
$decorated = new WsServer($controller);
|
||||||
|
$decorated->enableKeepAlive($this->_server->loop);
|
||||||
|
} else {
|
||||||
|
$decorated = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($httpHost === null) {
|
||||||
|
$httpHost = $this->httpHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedOrigins = array_values($allowedOrigins);
|
||||||
|
if (0 === count($allowedOrigins)) {
|
||||||
|
$allowedOrigins[] = $httpHost;
|
||||||
|
}
|
||||||
|
if ('*' !== $allowedOrigins[0]) {
|
||||||
|
$decorated = new OriginCheck($decorated, $allowedOrigins);
|
||||||
|
}
|
||||||
|
|
||||||
|
//allow origins in flash policy server
|
||||||
|
if(empty($this->flashServer) === false) {
|
||||||
|
foreach($allowedOrigins as $allowedOrgin) {
|
||||||
|
$this->flashServer->app->addAllowedAccess($allowedOrgin, $this->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->routes->add('rr-' . ++$this->_routeCounter, new Route($path, array('_controller' => $decorated), array('Origin' => $this->httpHost), array(), $httpHost, array(), array('GET')));
|
||||||
|
|
||||||
|
return $decorated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the server by entering the event loop
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
$this->_server->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php
vendored
Normal file
31
vendor/cboden/ratchet/src/Ratchet/ComponentInterface.php
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the interface to build a Ratchet application with.
|
||||||
|
* It implements the decorator pattern to build an application stack
|
||||||
|
*/
|
||||||
|
interface ComponentInterface {
|
||||||
|
/**
|
||||||
|
* When a new connection is opened it will be passed to this method
|
||||||
|
* @param ConnectionInterface $conn The socket/connection that just connected to your application
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onOpen(ConnectionInterface $conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called before or after a socket is closed (depends on how it's closed). SendMessage to $conn will not result in an error if it has already been closed.
|
||||||
|
* @param ConnectionInterface $conn The socket/connection that is closing/closed
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there is an error with one of the sockets, or somewhere in the application where an Exception is thrown,
|
||||||
|
* the Exception is sent back down the stack, handled by the Server and bubbled back up the application through this method
|
||||||
|
* @param ConnectionInterface $conn
|
||||||
|
* @param \Exception $e
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e);
|
||||||
|
}
|
||||||
26
vendor/cboden/ratchet/src/Ratchet/ConnectionInterface.php
vendored
Normal file
26
vendor/cboden/ratchet/src/Ratchet/ConnectionInterface.php
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of Ratchet being used
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const VERSION = 'Ratchet/0.4.4';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
22
vendor/cboden/ratchet/src/Ratchet/Http/CloseResponseTrait.php
vendored
Normal file
22
vendor/cboden/ratchet/src/Ratchet/Http/CloseResponseTrait.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use GuzzleHttp\Psr7\Message;
|
||||||
|
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(Message::toString($response));
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php
vendored
Normal file
64
vendor/cboden/ratchet/src/Ratchet/Http/HttpRequestParser.php
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\MessageInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use GuzzleHttp\Psr7\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Message::parseRequest($headers);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php
vendored
Normal file
76
vendor/cboden/ratchet/src/Ratchet/Http/HttpServer.php
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class HttpServer implements MessageComponentInterface {
|
||||||
|
use CloseResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffers incoming HTTP requests returning a Guzzle Request when coalesced
|
||||||
|
* @var HttpRequestParser
|
||||||
|
* @note May not expose this in the future, may do through facade methods
|
||||||
|
*/
|
||||||
|
protected $_reqParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Http\HttpServerInterface
|
||||||
|
*/
|
||||||
|
protected $_httpServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HttpServerInterface
|
||||||
|
*/
|
||||||
|
public function __construct(HttpServerInterface $component) {
|
||||||
|
$this->_httpServer = $component;
|
||||||
|
$this->_reqParser = new HttpRequestParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$conn->httpHeadersReceived = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
if (true !== $from->httpHeadersReceived) {
|
||||||
|
try {
|
||||||
|
if (null === ($request = $this->_reqParser->onMessage($from, $msg))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (\OverflowException $oe) {
|
||||||
|
return $this->close($from, 413);
|
||||||
|
}
|
||||||
|
|
||||||
|
$from->httpHeadersReceived = true;
|
||||||
|
|
||||||
|
return $this->_httpServer->onOpen($from, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_httpServer->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
if ($conn->httpHeadersReceived) {
|
||||||
|
$this->_httpServer->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
if ($conn->httpHeadersReceived) {
|
||||||
|
$this->_httpServer->onError($conn, $e);
|
||||||
|
} else {
|
||||||
|
$this->close($conn, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php
vendored
Normal file
14
vendor/cboden/ratchet/src/Ratchet/Http/HttpServerInterface.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
interface HttpServerInterface extends MessageComponentInterface {
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param \Psr\Http\Message\RequestInterface $request null is default because PHP won't let me overload; don't pass null!!!
|
||||||
|
* @throws \UnexpectedValueException if a RequestInterface is not passed
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null);
|
||||||
|
}
|
||||||
18
vendor/cboden/ratchet/src/Ratchet/Http/NoOpHttpServerController.php
vendored
Normal file
18
vendor/cboden/ratchet/src/Ratchet/Http/NoOpHttpServerController.php
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
class NoOpHttpServerController implements HttpServerInterface {
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
65
vendor/cboden/ratchet/src/Ratchet/Http/OriginCheck.php
vendored
Normal file
65
vendor/cboden/ratchet/src/Ratchet/Http/OriginCheck.php
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A middleware to ensure JavaScript clients connecting are from the expected domain.
|
||||||
|
* This protects other websites from open WebSocket connections to your application.
|
||||||
|
* Note: This can be spoofed from non-web browser clients
|
||||||
|
*/
|
||||||
|
class OriginCheck implements HttpServerInterface {
|
||||||
|
use CloseResponseTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
protected $_component;
|
||||||
|
|
||||||
|
public $allowedOrigins = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param MessageComponentInterface $component Component/Application to decorate
|
||||||
|
* @param array $allowed An array of allowed domains that are allowed to connect from
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $component, array $allowed = []) {
|
||||||
|
$this->_component = $component;
|
||||||
|
$this->allowedOrigins += $allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
$header = (string)$request->getHeader('Origin')[0];
|
||||||
|
$origin = parse_url($header, PHP_URL_HOST) ?: $header;
|
||||||
|
|
||||||
|
if (!in_array($origin, $this->allowedOrigins)) {
|
||||||
|
return $this->close($conn, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_component->onOpen($conn, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
return $this->_component->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn) {
|
||||||
|
return $this->_component->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_component->onError($conn, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
96
vendor/cboden/ratchet/src/Ratchet/Http/Router.php
vendored
Normal file
96
vendor/cboden/ratchet/src/Ratchet/Http/Router.php
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||||
|
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||||
|
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||||
|
use GuzzleHttp\Psr7\Query;
|
||||||
|
|
||||||
|
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, Query::parse($uri->getQuery() ?: ''));
|
||||||
|
|
||||||
|
$request = $request->withUri($uri->withQuery(Query::build($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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php
vendored
Normal file
5
vendor/cboden/ratchet/src/Ratchet/MessageComponentInterface.php
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
interface MessageComponentInterface extends ComponentInterface, MessageInterface {
|
||||||
|
}
|
||||||
12
vendor/cboden/ratchet/src/Ratchet/MessageInterface.php
vendored
Normal file
12
vendor/cboden/ratchet/src/Ratchet/MessageInterface.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
interface MessageInterface {
|
||||||
|
/**
|
||||||
|
* Triggered when a client sends data through the socket
|
||||||
|
* @param \Ratchet\ConnectionInterface $from The socket/connection that sent the message to your application
|
||||||
|
* @param string $msg The message received
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg);
|
||||||
|
}
|
||||||
23
vendor/cboden/ratchet/src/Ratchet/Server/EchoServer.php
vendored
Normal file
23
vendor/cboden/ratchet/src/Ratchet/Server/EchoServer.php
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple Ratchet application that will reply to all messages with the message it received
|
||||||
|
*/
|
||||||
|
class EchoServer implements MessageComponentInterface {
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$from->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
200
vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php
vendored
Normal file
200
vendor/cboden/ratchet/src/Ratchet/Server/FlashPolicy.php
vendored
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An app to go on a server stack to pass a policy file to a Flash socket
|
||||||
|
* Useful if you're using Flash as a WebSocket polyfill on IE
|
||||||
|
* Be sure to run your server instance on port 843
|
||||||
|
* By default this lets accepts everything, make sure you tighten the rules up for production
|
||||||
|
* @final
|
||||||
|
* @link http://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
|
||||||
|
* @link http://learn.adobe.com/wiki/download/attachments/64389123/CrossDomain_PolicyFile_Specification.pdf?version=1
|
||||||
|
* @link view-source:http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd
|
||||||
|
*/
|
||||||
|
class FlashPolicy implements MessageComponentInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the root policy node
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_policy = '<?xml version="1.0"?><!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy></cross-domain-policy>';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an array of allowed domains and their ports
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $_access = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_siteControl = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_cache = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_cacheValid = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a domain to an allowed access list.
|
||||||
|
*
|
||||||
|
* @param string $domain Specifies a requesting domain to be granted access. Both named domains and IP
|
||||||
|
* addresses are acceptable values. Subdomains are considered different domains. A wildcard (*) can
|
||||||
|
* be used to match all domains when used alone, or multiple domains (subdomains) when used as a
|
||||||
|
* prefix for an explicit, second-level domain name separated with a dot (.)
|
||||||
|
* @param string $ports A comma-separated list of ports or range of ports that a socket connection
|
||||||
|
* is allowed to connect to. A range of ports is specified through a dash (-) between two port numbers.
|
||||||
|
* Ranges can be used with individual ports when separated with a comma. A single wildcard (*) can
|
||||||
|
* be used to allow all ports.
|
||||||
|
* @param bool $secure
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @return FlashPolicy
|
||||||
|
*/
|
||||||
|
public function addAllowedAccess($domain, $ports = '*', $secure = false) {
|
||||||
|
if (!$this->validateDomain($domain)) {
|
||||||
|
throw new \UnexpectedValueException('Invalid domain');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->validatePorts($ports)) {
|
||||||
|
throw new \UnexpectedValueException('Invalid Port');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_access[] = array($domain, $ports, (boolean)$secure);
|
||||||
|
$this->_cacheValid = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all domains from the allowed access list.
|
||||||
|
*
|
||||||
|
* @return \Ratchet\Server\FlashPolicy
|
||||||
|
*/
|
||||||
|
public function clearAllowedAccess() {
|
||||||
|
$this->_access = array();
|
||||||
|
$this->_cacheValid = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* site-control defines the meta-policy for the current domain. A meta-policy specifies acceptable
|
||||||
|
* domain policy files other than the master policy file located in the target domain's root and named
|
||||||
|
* crossdomain.xml.
|
||||||
|
*
|
||||||
|
* @param string $permittedCrossDomainPolicies
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @return FlashPolicy
|
||||||
|
*/
|
||||||
|
public function setSiteControl($permittedCrossDomainPolicies = 'all') {
|
||||||
|
if (!$this->validateSiteControl($permittedCrossDomainPolicies)) {
|
||||||
|
throw new \UnexpectedValueException('Invalid site control set');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_siteControl = $permittedCrossDomainPolicies;
|
||||||
|
$this->_cacheValid = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
if (!$this->_cacheValid) {
|
||||||
|
$this->_cache = $this->renderPolicy()->asXML();
|
||||||
|
$this->_cacheValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$from->send($this->_cache . "\0");
|
||||||
|
$from->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the crossdomain file based on the template policy
|
||||||
|
*
|
||||||
|
* @throws \UnexpectedValueException
|
||||||
|
* @return \SimpleXMLElement
|
||||||
|
*/
|
||||||
|
public function renderPolicy() {
|
||||||
|
$policy = new \SimpleXMLElement($this->_policy);
|
||||||
|
|
||||||
|
$siteControl = $policy->addChild('site-control');
|
||||||
|
|
||||||
|
if ($this->_siteControl == '') {
|
||||||
|
$this->setSiteControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
$siteControl->addAttribute('permitted-cross-domain-policies', $this->_siteControl);
|
||||||
|
|
||||||
|
if (empty($this->_access)) {
|
||||||
|
throw new \UnexpectedValueException('You must add a domain through addAllowedAccess()');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->_access as $access) {
|
||||||
|
$tmp = $policy->addChild('allow-access-from');
|
||||||
|
$tmp->addAttribute('domain', $access[0]);
|
||||||
|
$tmp->addAttribute('to-ports', $access[1]);
|
||||||
|
$tmp->addAttribute('secure', ($access[2] === true) ? 'true' : 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the proper site control was passed
|
||||||
|
*
|
||||||
|
* @param string $permittedCrossDomainPolicies
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validateSiteControl($permittedCrossDomainPolicies) {
|
||||||
|
//'by-content-type' and 'by-ftp-filename' are not available for sockets
|
||||||
|
return (bool)in_array($permittedCrossDomainPolicies, array('none', 'master-only', 'all'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate for proper domains (wildcards allowed)
|
||||||
|
*
|
||||||
|
* @param string $domain
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validateDomain($domain) {
|
||||||
|
return (bool)preg_match("/^((http(s)?:\/\/)?([a-z0-9-_]+\.|\*\.)*([a-z0-9-_\.]+)|\*)$/i", $domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure valid ports were passed
|
||||||
|
*
|
||||||
|
* @param string $port
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validatePorts($port) {
|
||||||
|
return (bool)preg_match('/^(\*|(\d+[,-]?)*\d+)$/', $port);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php
vendored
Normal file
38
vendor/cboden/ratchet/src/Ratchet/Server/IoConnection.php
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use React\Socket\ConnectionInterface as ReactConn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
class IoConnection implements ConnectionInterface {
|
||||||
|
/**
|
||||||
|
* @var \React\Socket\ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $conn;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function __construct(ReactConn $conn) {
|
||||||
|
$this->conn = $conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function send($data) {
|
||||||
|
$this->conn->write($data);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close() {
|
||||||
|
$this->conn->end();
|
||||||
|
}
|
||||||
|
}
|
||||||
140
vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php
vendored
Normal file
140
vendor/cboden/ratchet/src/Ratchet/Server/IoServer.php
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Socket\ServerInterface;
|
||||||
|
use React\EventLoop\Factory as LoopFactory;
|
||||||
|
use React\Socket\Server as Reactor;
|
||||||
|
use React\Socket\SecureServer as SecureReactor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an open-ended socket to listen on a port for incoming connections.
|
||||||
|
* Events are delegated through this to attached applications
|
||||||
|
*/
|
||||||
|
class IoServer {
|
||||||
|
/**
|
||||||
|
* @var \React\EventLoop\LoopInterface
|
||||||
|
*/
|
||||||
|
public $loop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
public $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The socket server the Ratchet Application is run off of
|
||||||
|
* @var \React\Socket\ServerInterface
|
||||||
|
*/
|
||||||
|
public $socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\MessageComponentInterface $app The Ratchet application stack to host
|
||||||
|
* @param \React\Socket\ServerInterface $socket The React socket server to run the Ratchet application off of
|
||||||
|
* @param \React\EventLoop\LoopInterface|null $loop The React looper to run the Ratchet application off of
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $app, ServerInterface $socket, LoopInterface $loop = null) {
|
||||||
|
if (false === strpos(PHP_VERSION, "hiphop")) {
|
||||||
|
gc_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
ob_implicit_flush();
|
||||||
|
|
||||||
|
$this->loop = $loop;
|
||||||
|
$this->app = $app;
|
||||||
|
$this->socket = $socket;
|
||||||
|
|
||||||
|
$socket->on('connection', array($this, 'handleConnect'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\MessageComponentInterface $component The application that I/O will call when events are received
|
||||||
|
* @param int $port The port to server sockets on
|
||||||
|
* @param string $address The address to receive sockets on (0.0.0.0 means receive connections from any)
|
||||||
|
* @return IoServer
|
||||||
|
*/
|
||||||
|
public static function factory(MessageComponentInterface $component, $port = 80, $address = '0.0.0.0') {
|
||||||
|
$loop = LoopFactory::create();
|
||||||
|
$socket = new Reactor($address . ':' . $port, $loop);
|
||||||
|
|
||||||
|
return new static($component, $socket, $loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the application by entering the event loop
|
||||||
|
* @throws \RuntimeException If a loop was not previously specified
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
if (null === $this->loop) {
|
||||||
|
throw new \RuntimeException("A React Loop was not provided during instantiation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
$this->loop->run();
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when a new connection is received from React
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleConnect($conn) {
|
||||||
|
$conn->decor = new IoConnection($conn);
|
||||||
|
$conn->decor->resourceId = (int)$conn->stream;
|
||||||
|
|
||||||
|
$uri = $conn->getRemoteAddress();
|
||||||
|
$conn->decor->remoteAddress = trim(
|
||||||
|
parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_HOST),
|
||||||
|
'[]'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app->onOpen($conn->decor);
|
||||||
|
|
||||||
|
$conn->on('data', function ($data) use ($conn) {
|
||||||
|
$this->handleData($data, $conn);
|
||||||
|
});
|
||||||
|
$conn->on('close', function () use ($conn) {
|
||||||
|
$this->handleEnd($conn);
|
||||||
|
});
|
||||||
|
$conn->on('error', function (\Exception $e) use ($conn) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data has been received from React
|
||||||
|
* @param string $data
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleData($data, $conn) {
|
||||||
|
try {
|
||||||
|
$this->app->onMessage($conn->decor, $data);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A connection has been closed by React
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleEnd($conn) {
|
||||||
|
try {
|
||||||
|
$this->app->onClose($conn->decor);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->handleError($e, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($conn->decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error has occurred, let the listening application know
|
||||||
|
* @param \Exception $e
|
||||||
|
* @param \React\Socket\ConnectionInterface $conn
|
||||||
|
*/
|
||||||
|
public function handleError(\Exception $e, $conn) {
|
||||||
|
$this->app->onError($conn->decor, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
111
vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php
vendored
Normal file
111
vendor/cboden/ratchet/src/Ratchet/Server/IpBlackList.php
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class IpBlackList implements MessageComponentInterface {
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $_blacklist = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
protected $_decorating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\MessageComponentInterface $component
|
||||||
|
*/
|
||||||
|
public function __construct(MessageComponentInterface $component) {
|
||||||
|
$this->_decorating = $component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an address to the blacklist that will not be allowed to connect to your application
|
||||||
|
* @param string $ip IP address to block from connecting to your application
|
||||||
|
* @return IpBlackList
|
||||||
|
*/
|
||||||
|
public function blockAddress($ip) {
|
||||||
|
$this->_blacklist[$ip] = true;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock an address so they can access your application again
|
||||||
|
* @param string $ip IP address to unblock from connecting to your application
|
||||||
|
* @return IpBlackList
|
||||||
|
*/
|
||||||
|
public function unblockAddress($ip) {
|
||||||
|
if (isset($this->_blacklist[$this->filterAddress($ip)])) {
|
||||||
|
unset($this->_blacklist[$this->filterAddress($ip)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $address
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isBlocked($address) {
|
||||||
|
return (isset($this->_blacklist[$this->filterAddress($address)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of all the addresses blocked
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getBlockedAddresses() {
|
||||||
|
return array_keys($this->_blacklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $address
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function filterAddress($address) {
|
||||||
|
if (strstr($address, ':') && substr_count($address, '.') == 3) {
|
||||||
|
list($address, $port) = explode(':', $address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onOpen(ConnectionInterface $conn) {
|
||||||
|
if ($this->isBlocked($conn->remoteAddress)) {
|
||||||
|
return $conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_decorating->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
return $this->_decorating->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn) {
|
||||||
|
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||||
|
$this->_decorating->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
if (!$this->isBlocked($conn->remoteAddress)) {
|
||||||
|
$this->_decorating->onError($conn, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php
vendored
Normal file
16
vendor/cboden/ratchet/src/Ratchet/Session/Serialize/HandlerInterface.php
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
|
interface HandlerInterface {
|
||||||
|
/**
|
||||||
|
* @param array
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function serialize(array $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function unserialize($raw);
|
||||||
|
}
|
||||||
33
vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpBinaryHandler.php
vendored
Normal file
33
vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpBinaryHandler.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
|
class PhpBinaryHandler implements HandlerInterface {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function serialize(array $data) {
|
||||||
|
throw new \RuntimeException("Serialize PhpHandler:serialize code not written yet, write me!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
|
||||||
|
*/
|
||||||
|
public function unserialize($raw) {
|
||||||
|
$returnData = array();
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
while ($offset < strlen($raw)) {
|
||||||
|
$num = ord($raw[$offset]);
|
||||||
|
$offset += 1;
|
||||||
|
$varname = substr($raw, $offset, $num);
|
||||||
|
$offset += $num;
|
||||||
|
$data = unserialize(substr($raw, $offset));
|
||||||
|
|
||||||
|
$returnData[$varname] = $data;
|
||||||
|
$offset += strlen(serialize($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnData;
|
||||||
|
}
|
||||||
|
}
|
||||||
49
vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpHandler.php
vendored
Normal file
49
vendor/cboden/ratchet/src/Ratchet/Session/Serialize/PhpHandler.php
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
|
||||||
|
class PhpHandler implements HandlerInterface {
|
||||||
|
/**
|
||||||
|
* Simply reverse behaviour of unserialize method.
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function serialize(array $data) {
|
||||||
|
$preSerialized = array();
|
||||||
|
$serialized = '';
|
||||||
|
|
||||||
|
if (count($data)) {
|
||||||
|
foreach ($data as $bucket => $bucketData) {
|
||||||
|
$preSerialized[] = $bucket . '|' . serialize($bucketData);
|
||||||
|
}
|
||||||
|
$serialized = implode('', $preSerialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @link http://ca2.php.net/manual/en/function.session-decode.php#108037 Code from this comment on php.net
|
||||||
|
* @throws \UnexpectedValueException If there is a problem parsing the data
|
||||||
|
*/
|
||||||
|
public function unserialize($raw) {
|
||||||
|
$returnData = array();
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
while ($offset < strlen($raw)) {
|
||||||
|
if (!strstr(substr($raw, $offset), "|")) {
|
||||||
|
throw new \UnexpectedValueException("invalid data, remaining: " . substr($raw, $offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = strpos($raw, "|", $offset);
|
||||||
|
$num = $pos - $offset;
|
||||||
|
$varname = substr($raw, $offset, $num);
|
||||||
|
$offset += $num + 1;
|
||||||
|
$data = unserialize(substr($raw, $offset));
|
||||||
|
|
||||||
|
$returnData[$varname] = $data;
|
||||||
|
$offset += strlen(serialize($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnData;
|
||||||
|
}
|
||||||
|
}
|
||||||
243
vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php
vendored
Normal file
243
vendor/cboden/ratchet/src/Ratchet/Session/SessionProvider.php
vendored
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\Http\HttpServerInterface;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Ratchet\Session\Storage\VirtualSessionStorage;
|
||||||
|
use Ratchet\Session\Serialize\HandlerInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component will allow access to session data from your website for each user connected
|
||||||
|
* Symfony HttpFoundation is required for this component to work
|
||||||
|
* Your website must also use Symfony HttpFoundation Sessions to read your sites session data
|
||||||
|
* If your are not using at least PHP 5.4 you must include a SessionHandlerInterface stub (is included in Symfony HttpFoundation, loaded w/ composer)
|
||||||
|
*/
|
||||||
|
class SessionProvider implements HttpServerInterface {
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\MessageComponentInterface
|
||||||
|
*/
|
||||||
|
protected $_app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selected handler storage assigned by the developer
|
||||||
|
* @var \SessionHandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Null storage handler if no previous session was found
|
||||||
|
* @var \SessionHandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Session\Serialize\HandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Ratchet\Http\HttpServerInterface $app
|
||||||
|
* @param \SessionHandlerInterface $handler
|
||||||
|
* @param array $options
|
||||||
|
* @param \Ratchet\Session\Serialize\HandlerInterface $serializer
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*/
|
||||||
|
public function __construct(HttpServerInterface $app, \SessionHandlerInterface $handler, array $options = array(), HandlerInterface $serializer = null) {
|
||||||
|
$this->_app = $app;
|
||||||
|
$this->_handler = $handler;
|
||||||
|
$this->_null = new NullSessionHandler;
|
||||||
|
|
||||||
|
ini_set('session.auto_start', 0);
|
||||||
|
ini_set('session.cache_limiter', '');
|
||||||
|
ini_set('session.use_cookies', 0);
|
||||||
|
|
||||||
|
$this->setOptions($options);
|
||||||
|
|
||||||
|
if (null === $serializer) {
|
||||||
|
$serialClass = __NAMESPACE__ . "\\Serialize\\{$this->toClassCase(ini_get('session.serialize_handler'))}Handler"; // awesome/terrible hack, eh?
|
||||||
|
if (!class_exists($serialClass)) {
|
||||||
|
throw new \RuntimeException('Unable to parse session serialize handler');
|
||||||
|
}
|
||||||
|
|
||||||
|
$serializer = new $serialClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_serializer = $serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) {
|
||||||
|
$sessionName = ini_get('session.name');
|
||||||
|
|
||||||
|
$id = array_reduce($request->getHeader('Cookie'), function($accumulator, $cookie) use ($sessionName) {
|
||||||
|
if ($accumulator) {
|
||||||
|
return $accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
$crumbs = $this->parseCookie($cookie);
|
||||||
|
|
||||||
|
return isset($crumbs['cookies'][$sessionName]) ? $crumbs['cookies'][$sessionName] : false;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
if (null === $request || false === $id) {
|
||||||
|
$saveHandler = $this->_null;
|
||||||
|
$id = '';
|
||||||
|
} else {
|
||||||
|
$saveHandler = $this->_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conn->Session = new Session(new VirtualSessionStorage($saveHandler, $id, $this->_serializer));
|
||||||
|
|
||||||
|
if (ini_get('session.auto_start')) {
|
||||||
|
$conn->Session->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_app->onOpen($conn, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
return $this->_app->onMessage($from, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onClose(ConnectionInterface $conn) {
|
||||||
|
// "close" session for Connection
|
||||||
|
|
||||||
|
return $this->_app->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_app->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all the php session. ini options
|
||||||
|
* © Symfony
|
||||||
|
* @param array $options
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function setOptions(array $options) {
|
||||||
|
$all = array(
|
||||||
|
'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
|
||||||
|
'cookie_lifetime', 'cookie_path', 'cookie_secure',
|
||||||
|
'entropy_file', 'entropy_length', 'gc_divisor',
|
||||||
|
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
|
||||||
|
'hash_function', 'name', 'referer_check',
|
||||||
|
'serialize_handler', 'use_cookies',
|
||||||
|
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
|
||||||
|
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
|
||||||
|
'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($all as $key) {
|
||||||
|
if (!array_key_exists($key, $options)) {
|
||||||
|
$options[$key] = ini_get("session.{$key}");
|
||||||
|
} else {
|
||||||
|
ini_set("session.{$key}", $options[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $langDef Input to convert
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function toClassCase($langDef) {
|
||||||
|
return str_replace(' ', '', ucwords(str_replace('_', ' ', $langDef)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from Guzzle3
|
||||||
|
*/
|
||||||
|
private static $cookieParts = array(
|
||||||
|
'domain' => 'Domain',
|
||||||
|
'path' => 'Path',
|
||||||
|
'max_age' => 'Max-Age',
|
||||||
|
'expires' => 'Expires',
|
||||||
|
'version' => 'Version',
|
||||||
|
'secure' => 'Secure',
|
||||||
|
'port' => 'Port',
|
||||||
|
'discard' => 'Discard',
|
||||||
|
'comment' => 'Comment',
|
||||||
|
'comment_url' => 'Comment-Url',
|
||||||
|
'http_only' => 'HttpOnly'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from Guzzle3
|
||||||
|
*/
|
||||||
|
private function parseCookie($cookie, $host = null, $path = null, $decode = false) {
|
||||||
|
// Explode the cookie string using a series of semicolons
|
||||||
|
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
|
||||||
|
|
||||||
|
// The name of the cookie (first kvp) must include an equal sign.
|
||||||
|
if (empty($pieces) || !strpos($pieces[0], '=')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the default return array
|
||||||
|
$data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array(
|
||||||
|
'cookies' => array(),
|
||||||
|
'data' => array(),
|
||||||
|
'path' => $path ?: '/',
|
||||||
|
'http_only' => false,
|
||||||
|
'discard' => false,
|
||||||
|
'domain' => $host
|
||||||
|
));
|
||||||
|
$foundNonCookies = 0;
|
||||||
|
|
||||||
|
// Add the cookie pieces into the parsed data array
|
||||||
|
foreach ($pieces as $part) {
|
||||||
|
|
||||||
|
$cookieParts = explode('=', $part, 2);
|
||||||
|
$key = trim($cookieParts[0]);
|
||||||
|
|
||||||
|
if (count($cookieParts) == 1) {
|
||||||
|
// Can be a single value (e.g. secure, httpOnly)
|
||||||
|
$value = true;
|
||||||
|
} else {
|
||||||
|
// Be sure to strip wrapping quotes
|
||||||
|
$value = trim($cookieParts[1], " \n\r\t\0\x0B\"");
|
||||||
|
if ($decode) {
|
||||||
|
$value = urldecode($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check for non-cookies when cookies have been found
|
||||||
|
if (!empty($data['cookies'])) {
|
||||||
|
foreach (self::$cookieParts as $mapValue => $search) {
|
||||||
|
if (!strcasecmp($search, $key)) {
|
||||||
|
$data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value;
|
||||||
|
$foundNonCookies++;
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a
|
||||||
|
// cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data.
|
||||||
|
$data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the expires date
|
||||||
|
if (!$data['expires'] && $data['max_age']) {
|
||||||
|
$data['expires'] = time() + (int) $data['max_age'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php
vendored
Normal file
54
vendor/cboden/ratchet/src/Ratchet/Session/Storage/Proxy/VirtualProxy.php
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage\Proxy;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
|
||||||
|
|
||||||
|
class VirtualProxy extends SessionHandlerProxy {
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_sessionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __construct(\SessionHandlerInterface $handler) {
|
||||||
|
parent::__construct($handler);
|
||||||
|
|
||||||
|
$this->saveHandlerName = 'user';
|
||||||
|
$this->_sessionName = ini_get('session.name');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
return $this->_sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setId($id) {
|
||||||
|
$this->_sessionId = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getName() {
|
||||||
|
return $this->_sessionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT CALL THIS METHOD
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setName($name) {
|
||||||
|
throw new \RuntimeException("Can not change session name in VirtualProxy");
|
||||||
|
}
|
||||||
|
}
|
||||||
88
vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php
vendored
Normal file
88
vendor/cboden/ratchet/src/Ratchet/Session/Storage/VirtualSessionStorage.php
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
|
||||||
|
use Ratchet\Session\Storage\Proxy\VirtualProxy;
|
||||||
|
use Ratchet\Session\Serialize\HandlerInterface;
|
||||||
|
|
||||||
|
class VirtualSessionStorage extends NativeSessionStorage {
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Session\Serialize\HandlerInterface
|
||||||
|
*/
|
||||||
|
protected $_serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \SessionHandlerInterface $handler
|
||||||
|
* @param string $sessionId The ID of the session to retrieve
|
||||||
|
* @param \Ratchet\Session\Serialize\HandlerInterface $serializer
|
||||||
|
*/
|
||||||
|
public function __construct(\SessionHandlerInterface $handler, $sessionId, HandlerInterface $serializer) {
|
||||||
|
$this->setSaveHandler($handler);
|
||||||
|
$this->saveHandler->setId($sessionId);
|
||||||
|
$this->_serializer = $serializer;
|
||||||
|
$this->setMetadataBag(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function start() {
|
||||||
|
if ($this->started && !$this->closed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You have to call Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::open() to use
|
||||||
|
// pdo_sqlite (and possible pdo_*) as session storage, if you are using a DSN string instead of a \PDO object
|
||||||
|
// in the constructor. The method arguments are filled with the values, which are also used by the symfony
|
||||||
|
// framework in this case. This must not be the best choice, but it works.
|
||||||
|
$this->saveHandler->open(session_save_path(), session_name());
|
||||||
|
|
||||||
|
$rawData = $this->saveHandler->read($this->saveHandler->getId());
|
||||||
|
$sessionData = $this->_serializer->unserialize($rawData);
|
||||||
|
|
||||||
|
$this->loadSession($sessionData);
|
||||||
|
|
||||||
|
if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) {
|
||||||
|
$this->saveHandler->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function regenerate($destroy = false, $lifetime = null) {
|
||||||
|
// .. ?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function save() {
|
||||||
|
// get the data from the bags?
|
||||||
|
// serialize the data
|
||||||
|
// save the data using the saveHandler
|
||||||
|
// $this->saveHandler->write($this->saveHandler->getId(),
|
||||||
|
|
||||||
|
if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) {
|
||||||
|
$this->saveHandler->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setSaveHandler($saveHandler = null) {
|
||||||
|
if (!($saveHandler instanceof \SessionHandlerInterface)) {
|
||||||
|
throw new \InvalidArgumentException('Handler must be instance of SessionHandlerInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($saveHandler instanceof VirtualProxy)) {
|
||||||
|
$saveHandler = new VirtualProxy($saveHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveHandler = $saveHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php
vendored
Normal file
5
vendor/cboden/ratchet/src/Ratchet/Wamp/Exception.php
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
class Exception extends \Exception {
|
||||||
|
}
|
||||||
31
vendor/cboden/ratchet/src/Ratchet/Wamp/JsonException.php
vendored
Normal file
31
vendor/cboden/ratchet/src/Ratchet/Wamp/JsonException.php
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
class JsonException extends Exception {
|
||||||
|
public function __construct() {
|
||||||
|
$code = json_last_error();
|
||||||
|
|
||||||
|
switch ($code) {
|
||||||
|
case JSON_ERROR_DEPTH:
|
||||||
|
$msg = 'Maximum stack depth exceeded';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_STATE_MISMATCH:
|
||||||
|
$msg = 'Underflow or the modes mismatch';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_CTRL_CHAR:
|
||||||
|
$msg = 'Unexpected control character found';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_SYNTAX:
|
||||||
|
$msg = 'Syntax error, malformed JSON';
|
||||||
|
break;
|
||||||
|
case JSON_ERROR_UTF8:
|
||||||
|
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$msg = 'Unknown error';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($msg, $code);
|
||||||
|
}
|
||||||
|
}
|
||||||
161
vendor/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php
vendored
Normal file
161
vendor/cboden/ratchet/src/Ratchet/Wamp/ServerProtocol.php
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket Application Messaging Protocol
|
||||||
|
*
|
||||||
|
* @link http://wamp.ws/spec
|
||||||
|
* @link https://github.com/oberstet/autobahn-js
|
||||||
|
*
|
||||||
|
* +--------------+----+------------------+
|
||||||
|
* | Message Type | ID | DIRECTION |
|
||||||
|
* |--------------+----+------------------+
|
||||||
|
* | WELCOME | 0 | Server-to-Client |
|
||||||
|
* | PREFIX | 1 | Bi-Directional |
|
||||||
|
* | CALL | 2 | Client-to-Server |
|
||||||
|
* | CALL RESULT | 3 | Server-to-Client |
|
||||||
|
* | CALL ERROR | 4 | Server-to-Client |
|
||||||
|
* | SUBSCRIBE | 5 | Client-to-Server |
|
||||||
|
* | UNSUBSCRIBE | 6 | Client-to-Server |
|
||||||
|
* | PUBLISH | 7 | Client-to-Server |
|
||||||
|
* | EVENT | 8 | Server-to-Client |
|
||||||
|
* +--------------+----+------------------+
|
||||||
|
*/
|
||||||
|
class ServerProtocol implements MessageComponentInterface, WsServerInterface {
|
||||||
|
const MSG_WELCOME = 0;
|
||||||
|
const MSG_PREFIX = 1;
|
||||||
|
const MSG_CALL = 2;
|
||||||
|
const MSG_CALL_RESULT = 3;
|
||||||
|
const MSG_CALL_ERROR = 4;
|
||||||
|
const MSG_SUBSCRIBE = 5;
|
||||||
|
const MSG_UNSUBSCRIBE = 6;
|
||||||
|
const MSG_PUBLISH = 7;
|
||||||
|
const MSG_EVENT = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var WampServerInterface
|
||||||
|
*/
|
||||||
|
protected $_decorating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \SplObjectStorage
|
||||||
|
*/
|
||||||
|
protected $connections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WampServerInterface $serverComponent An class to propagate calls through
|
||||||
|
*/
|
||||||
|
public function __construct(WampServerInterface $serverComponent) {
|
||||||
|
$this->_decorating = $serverComponent;
|
||||||
|
$this->connections = new \SplObjectStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocols() {
|
||||||
|
if ($this->_decorating instanceof WsServerInterface) {
|
||||||
|
$subs = $this->_decorating->getSubProtocols();
|
||||||
|
$subs[] = 'wamp';
|
||||||
|
|
||||||
|
return $subs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['wamp'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$decor = new WampConnection($conn);
|
||||||
|
$this->connections->attach($conn, $decor);
|
||||||
|
|
||||||
|
$this->_decorating->onOpen($decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @throws \Ratchet\Wamp\Exception
|
||||||
|
* @throws \Ratchet\Wamp\JsonException
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$from = $this->connections[$from];
|
||||||
|
|
||||||
|
if (null === ($json = @json_decode($msg, true))) {
|
||||||
|
throw new JsonException;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($json) || $json !== array_values($json)) {
|
||||||
|
throw new Exception("Invalid WAMP message format");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($json[1]) && !(is_string($json[1]) || is_numeric($json[1]))) {
|
||||||
|
throw new Exception('Invalid Topic, must be a string');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($json[0]) {
|
||||||
|
case static::MSG_PREFIX:
|
||||||
|
$from->WAMP->prefixes[$json[1]] = $json[2];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_CALL:
|
||||||
|
array_shift($json);
|
||||||
|
$callID = array_shift($json);
|
||||||
|
$procURI = array_shift($json);
|
||||||
|
|
||||||
|
if (count($json) == 1 && is_array($json[0])) {
|
||||||
|
$json = $json[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_decorating->onCall($from, $callID, $from->getUri($procURI), $json);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_SUBSCRIBE:
|
||||||
|
$this->_decorating->onSubscribe($from, $from->getUri($json[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_UNSUBSCRIBE:
|
||||||
|
$this->_decorating->onUnSubscribe($from, $from->getUri($json[1]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::MSG_PUBLISH:
|
||||||
|
$exclude = (array_key_exists(3, $json) ? $json[3] : null);
|
||||||
|
if (!is_array($exclude)) {
|
||||||
|
if (true === (boolean)$exclude) {
|
||||||
|
$exclude = [$from->WAMP->sessionId];
|
||||||
|
} else {
|
||||||
|
$exclude = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$eligible = (array_key_exists(4, $json) ? $json[4] : []);
|
||||||
|
|
||||||
|
$this->_decorating->onPublish($from, $from->getUri($json[1]), $json[2], $exclude, $eligible);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Invalid WAMP message type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$decor = $this->connections[$conn];
|
||||||
|
$this->connections->detach($conn);
|
||||||
|
|
||||||
|
$this->_decorating->onClose($decor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
return $this->_decorating->onError($this->connections[$conn], $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
101
vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php
vendored
Normal file
101
vendor/cboden/ratchet/src/Ratchet/Wamp/Topic.php
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?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}
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function getIterator() {
|
||||||
|
return $this->subscribers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
#[\ReturnTypeWillChange]
|
||||||
|
public function count() {
|
||||||
|
return $this->subscribers->count();
|
||||||
|
}
|
||||||
|
}
|
||||||
125
vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php
vendored
Normal file
125
vendor/cboden/ratchet/src/Ratchet/Wamp/TopicManager.php
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
|
||||||
|
class TopicManager implements WsServerInterface, WampServerInterface {
|
||||||
|
/**
|
||||||
|
* @var WampServerInterface
|
||||||
|
*/
|
||||||
|
protected $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $topicLookup = array();
|
||||||
|
|
||||||
|
public function __construct(WampServerInterface $app) {
|
||||||
|
$this->app = $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$conn->WAMP->subscriptions = new \SplObjectStorage;
|
||||||
|
$this->app->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
|
||||||
|
$this->app->onCall($conn, $id, $this->getTopic($topic), $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onSubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$topicObj = $this->getTopic($topic);
|
||||||
|
|
||||||
|
if ($conn->WAMP->subscriptions->contains($topicObj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->topicLookup[$topic]->add($conn);
|
||||||
|
$conn->WAMP->subscriptions->attach($topicObj);
|
||||||
|
$this->app->onSubscribe($conn, $topicObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onUnsubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$topicObj = $this->getTopic($topic);
|
||||||
|
|
||||||
|
if (!$conn->WAMP->subscriptions->contains($topicObj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cleanTopic($topicObj, $conn);
|
||||||
|
|
||||||
|
$this->app->onUnsubscribe($conn, $topicObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
|
||||||
|
$this->app->onPublish($conn, $this->getTopic($topic), $event, $exclude, $eligible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->app->onClose($conn);
|
||||||
|
|
||||||
|
foreach ($this->topicLookup as $topic) {
|
||||||
|
$this->cleanTopic($topic, $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->app->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocols() {
|
||||||
|
if ($this->app instanceof WsServerInterface) {
|
||||||
|
return $this->app->getSubProtocols();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string
|
||||||
|
* @return Topic
|
||||||
|
*/
|
||||||
|
protected function getTopic($topic) {
|
||||||
|
if (!array_key_exists($topic, $this->topicLookup)) {
|
||||||
|
$this->topicLookup[$topic] = new Topic($topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->topicLookup[$topic];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function cleanTopic(Topic $topic, ConnectionInterface $conn) {
|
||||||
|
if ($conn->WAMP->subscriptions->contains($topic)) {
|
||||||
|
$conn->WAMP->subscriptions->detach($topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->topicLookup[$topic->getId()]->remove($conn);
|
||||||
|
|
||||||
|
if (0 === $topic->count()) {
|
||||||
|
unset($this->topicLookup[$topic->getId()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php
vendored
Normal file
115
vendor/cboden/ratchet/src/Ratchet/Wamp/WampConnection.php
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
use Ratchet\Wamp\ServerProtocol as WAMP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ConnectionInterface object wrapper that is passed to your WAMP application
|
||||||
|
* representing a client. Methods on this Connection are therefore different.
|
||||||
|
* @property \stdClass $WAMP
|
||||||
|
*/
|
||||||
|
class WampConnection extends AbstractConnectionDecorator {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __construct(ConnectionInterface $conn) {
|
||||||
|
parent::__construct($conn);
|
||||||
|
|
||||||
|
$this->WAMP = new \StdClass;
|
||||||
|
$this->WAMP->sessionId = str_replace('.', '', uniqid(mt_rand(), true));
|
||||||
|
$this->WAMP->prefixes = array();
|
||||||
|
|
||||||
|
$this->send(json_encode(array(WAMP::MSG_WELCOME, $this->WAMP->sessionId, 1, \Ratchet\VERSION)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successfully respond to a call made by the client
|
||||||
|
* @param string $id The unique ID given by the client to respond to
|
||||||
|
* @param array $data an object or array
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function callResult($id, $data = array()) {
|
||||||
|
return $this->send(json_encode(array(WAMP::MSG_CALL_RESULT, $id, $data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond with an error to a client call
|
||||||
|
* @param string $id The unique ID given by the client to respond to
|
||||||
|
* @param string $errorUri The URI given to identify the specific error
|
||||||
|
* @param string $desc A developer-oriented description of the error
|
||||||
|
* @param string $details An optional human readable detail message to send back
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function callError($id, $errorUri, $desc = '', $details = null) {
|
||||||
|
if ($errorUri instanceof Topic) {
|
||||||
|
$errorUri = (string)$errorUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(WAMP::MSG_CALL_ERROR, $id, $errorUri, $desc);
|
||||||
|
|
||||||
|
if (null !== $details) {
|
||||||
|
$data[] = $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->send(json_encode($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $topic The topic to broadcast to
|
||||||
|
* @param mixed $msg Data to send with the event. Anything that is json'able
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function event($topic, $msg) {
|
||||||
|
return $this->send(json_encode(array(WAMP::MSG_EVENT, (string)$topic, $msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $curie
|
||||||
|
* @param string $uri
|
||||||
|
* @return WampConnection
|
||||||
|
*/
|
||||||
|
public function prefix($curie, $uri) {
|
||||||
|
$this->WAMP->prefixes[$curie] = (string)$uri;
|
||||||
|
|
||||||
|
return $this->send(json_encode(array(WAMP::MSG_PREFIX, $curie, (string)$uri)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full request URI from the connection object if a prefix has been established for it
|
||||||
|
* @param string $uri
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getUri($uri) {
|
||||||
|
$curieSeperator = ':';
|
||||||
|
|
||||||
|
if (preg_match('/http(s*)\:\/\//', $uri) == false) {
|
||||||
|
if (strpos($uri, $curieSeperator) !== false) {
|
||||||
|
list($prefix, $action) = explode($curieSeperator, $uri);
|
||||||
|
|
||||||
|
if(isset($this->WAMP->prefixes[$prefix]) === true){
|
||||||
|
return $this->WAMP->prefixes[$prefix] . '#' . $action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function send($data) {
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close($opt = null) {
|
||||||
|
$this->getConnection()->close($opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
67
vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php
vendored
Normal file
67
vendor/cboden/ratchet/src/Ratchet/Wamp/WampServer.php
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable support for the official WAMP sub-protocol in your application
|
||||||
|
* WAMP allows for Pub/Sub and RPC
|
||||||
|
* @link http://wamp.ws The WAMP specification
|
||||||
|
* @link https://github.com/oberstet/autobahn-js Souce for client side library
|
||||||
|
* @link http://autobahn.s3.amazonaws.com/js/autobahn.min.js Minified client side library
|
||||||
|
*/
|
||||||
|
class WampServer implements MessageComponentInterface, WsServerInterface {
|
||||||
|
/**
|
||||||
|
* @var ServerProtocol
|
||||||
|
*/
|
||||||
|
protected $wampProtocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class just makes it 1 step easier to use Topic objects in WAMP
|
||||||
|
* If you're looking at the source code, look in the __construct of this
|
||||||
|
* class and use that to make your application instead of using this
|
||||||
|
*/
|
||||||
|
public function __construct(WampServerInterface $app) {
|
||||||
|
$this->wampProtocol = new ServerProtocol(new TopicManager($app));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->wampProtocol->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onMessage(ConnectionInterface $conn, $msg) {
|
||||||
|
try {
|
||||||
|
$this->wampProtocol->onMessage($conn, $msg);
|
||||||
|
} catch (Exception $we) {
|
||||||
|
$conn->close(1007);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->wampProtocol->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->wampProtocol->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return $this->wampProtocol->getSubProtocols();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php
vendored
Normal file
43
vendor/cboden/ratchet/src/Ratchet/Wamp/WampServerInterface.php
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\ComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of Ratchet\ComponentInterface to server a WAMP application
|
||||||
|
* onMessage is replaced by various types of messages for this protocol (pub/sub or rpc)
|
||||||
|
*/
|
||||||
|
interface WampServerInterface extends ComponentInterface {
|
||||||
|
/**
|
||||||
|
* An RPC call has been received
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string $id The unique ID of the RPC, required to respond to
|
||||||
|
* @param string|Topic $topic The topic to execute the call against
|
||||||
|
* @param array $params Call parameters received from the client
|
||||||
|
*/
|
||||||
|
function onCall(ConnectionInterface $conn, $id, $topic, array $params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to subscribe to a topic has been made
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string|Topic $topic The topic to subscribe to
|
||||||
|
*/
|
||||||
|
function onSubscribe(ConnectionInterface $conn, $topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to unsubscribe from a topic has been made
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string|Topic $topic The topic to unsubscribe from
|
||||||
|
*/
|
||||||
|
function onUnSubscribe(ConnectionInterface $conn, $topic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client is attempting to publish content to a subscribed connections on a URI
|
||||||
|
* @param \Ratchet\ConnectionInterface $conn
|
||||||
|
* @param string|Topic $topic The topic the user has attempted to publish to
|
||||||
|
* @param string $event Payload of the publish
|
||||||
|
* @param array $exclude A list of session IDs the message should be excluded from (blacklist)
|
||||||
|
* @param array $eligible A list of session Ids the message should be send to (whitelist)
|
||||||
|
*/
|
||||||
|
function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible);
|
||||||
|
}
|
||||||
20
vendor/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php
vendored
Normal file
20
vendor/cboden/ratchet/src/Ratchet/WebSocket/ConnContext.php
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||||
|
|
||||||
|
class ConnContext {
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\WebSocket\WsConnection
|
||||||
|
*/
|
||||||
|
public $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||||
|
*/
|
||||||
|
public $buffer;
|
||||||
|
|
||||||
|
public function __construct(WsConnection $conn, MessageBuffer $buffer) {
|
||||||
|
$this->connection = $conn;
|
||||||
|
$this->buffer = $buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php
vendored
Normal file
8
vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageCallableInterface.php
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||||
|
|
||||||
|
interface MessageCallableInterface {
|
||||||
|
public function onMessage(ConnectionInterface $conn, MessageInterface $msg);
|
||||||
|
}
|
||||||
6
vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageComponentInterface.php
vendored
Normal file
6
vendor/cboden/ratchet/src/Ratchet/WebSocket/MessageComponentInterface.php
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\ComponentInterface;
|
||||||
|
|
||||||
|
interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface {
|
||||||
|
}
|
||||||
45
vendor/cboden/ratchet/src/Ratchet/WebSocket/WsConnection.php
vendored
Normal file
45
vendor/cboden/ratchet/src/Ratchet/WebSocket/WsConnection.php
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
use Ratchet\RFC6455\Messaging\DataInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\Frame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
* @property \StdClass $WebSocket
|
||||||
|
*/
|
||||||
|
class WsConnection extends AbstractConnectionDecorator {
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function send($msg) {
|
||||||
|
if (!$this->WebSocket->closing) {
|
||||||
|
if (!($msg instanceof DataInterface)) {
|
||||||
|
$msg = new Frame($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getConnection()->send($msg->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|\Ratchet\RFC6455\Messaging\DataInterface
|
||||||
|
*/
|
||||||
|
public function close($code = 1000) {
|
||||||
|
if ($this->WebSocket->closing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code instanceof DataInterface) {
|
||||||
|
$this->send($code);
|
||||||
|
} else {
|
||||||
|
$this->send(new Frame(pack('n', $code), true, Frame::OP_CLOSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getConnection()->close();
|
||||||
|
|
||||||
|
$this->WebSocket->closing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
225
vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php
vendored
Normal file
225
vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServer.php
vendored
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
use Ratchet\ComponentInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\MessageComponentInterface as DataComponentInterface;
|
||||||
|
use Ratchet\Http\HttpServerInterface;
|
||||||
|
use Ratchet\Http\CloseResponseTrait;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\FrameInterface;
|
||||||
|
use Ratchet\RFC6455\Messaging\Frame;
|
||||||
|
use Ratchet\RFC6455\Messaging\MessageBuffer;
|
||||||
|
use Ratchet\RFC6455\Messaging\CloseFrameChecker;
|
||||||
|
use Ratchet\RFC6455\Handshake\ServerNegotiator;
|
||||||
|
use Ratchet\RFC6455\Handshake\RequestVerifier;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use GuzzleHttp\Psr7\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(Message::toString($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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
14
vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php
vendored
Normal file
14
vendor/cboden/ratchet/src/Ratchet/WebSocket/WsServerInterface.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket Server Interface
|
||||||
|
*/
|
||||||
|
interface WsServerInterface {
|
||||||
|
/**
|
||||||
|
* If any component in a stack supports a WebSocket sub-protocol return each supported in an array
|
||||||
|
* @return array
|
||||||
|
* @todo This method may be removed in future version (note that will not break code, just make some code obsolete)
|
||||||
|
*/
|
||||||
|
function getSubProtocols();
|
||||||
|
}
|
||||||
36
vendor/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php
vendored
Normal file
36
vendor/cboden/ratchet/tests/autobahn/bin/fuzzingserver.php
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
class BinaryEcho implements \Ratchet\WebSocket\MessageComponentInterface {
|
||||||
|
public function onMessage(ConnectionInterface $from, \Ratchet\RFC6455\Messaging\MessageInterface $msg) {
|
||||||
|
$from->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$port = $argc > 1 ? $argv[1] : 8000;
|
||||||
|
$impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect');
|
||||||
|
|
||||||
|
$loop = new $impl;
|
||||||
|
$sock = new React\Socket\Server('0.0.0.0:' . $port, $loop);
|
||||||
|
|
||||||
|
$wsServer = new Ratchet\WebSocket\WsServer(new BinaryEcho);
|
||||||
|
// This is enabled to test https://github.com/ratchetphp/Ratchet/issues/430
|
||||||
|
// The time is left at 10 minutes so that it will not try to every ping anything
|
||||||
|
// This causes the Ratchet server to crash on test 2.7
|
||||||
|
$wsServer->enableKeepAlive($loop, 600);
|
||||||
|
|
||||||
|
$app = new Ratchet\Http\HttpServer($wsServer);
|
||||||
|
|
||||||
|
$server = new Ratchet\Server\IoServer($app, $sock, $loop);
|
||||||
|
$server->run();
|
||||||
15
vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json
vendored
Normal file
15
vendor/cboden/ratchet/tests/autobahn/fuzzingclient-all.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"options": {"failByDrop": false}
|
||||||
|
, "outdir": "reports/ab"
|
||||||
|
|
||||||
|
, "servers": [
|
||||||
|
{"agent": "Ratchet/0.4 libevent", "url": "ws://localhost:8001", "options": {"version": 18}}
|
||||||
|
, {"agent": "Ratchet/0.4 libev", "url": "ws://localhost:8004", "options": {"version": 18}}
|
||||||
|
, {"agent": "Ratchet/0.4 streams", "url": "ws://localhost:8002", "options": {"version": 18}}
|
||||||
|
, {"agent": "AutobahnTestSuite/0.5.9", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||||
|
]
|
||||||
|
|
||||||
|
, "cases": ["*"]
|
||||||
|
, "exclude-cases": []
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
|
}
|
||||||
12
vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json
vendored
Normal file
12
vendor/cboden/ratchet/tests/autobahn/fuzzingclient-profile.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"options": {"failByDrop": false}
|
||||||
|
, "outdir": "reports/profile"
|
||||||
|
|
||||||
|
, "servers": [
|
||||||
|
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||||
|
]
|
||||||
|
|
||||||
|
, "cases": ["9.7.4"]
|
||||||
|
, "exclude-cases": ["1.2.*", "2.3", "2.4", "2.6", "9.2.*", "9.4.*", "9.6.*", "9.8.*"]
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
|
}
|
||||||
12
vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json
vendored
Normal file
12
vendor/cboden/ratchet/tests/autobahn/fuzzingclient-quick.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"options": {"failByDrop": false}
|
||||||
|
, "outdir": "reports/rfc"
|
||||||
|
|
||||||
|
, "servers": [
|
||||||
|
{"agent": "Ratchet", "url": "ws://localhost:8000", "options": {"version": 18}}
|
||||||
|
]
|
||||||
|
|
||||||
|
, "cases": ["*"]
|
||||||
|
, "exclude-cases": []
|
||||||
|
, "exclude-agent-cases": {}
|
||||||
|
}
|
||||||
4
vendor/cboden/ratchet/tests/bootstrap.php
vendored
Normal file
4
vendor/cboden/ratchet/tests/bootstrap.php
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$loader = require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
$loader->addPsr4('Ratchet\\', __DIR__ . '/helpers/Ratchet');
|
||||||
50
vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php
vendored
Normal file
50
vendor/cboden/ratchet/tests/helpers/Ratchet/AbstractMessageComponentTestCase.php
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
|
||||||
|
abstract class AbstractMessageComponentTestCase extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_app;
|
||||||
|
protected $_serv;
|
||||||
|
protected $_conn;
|
||||||
|
|
||||||
|
abstract public function getConnectionClassString();
|
||||||
|
abstract public function getDecoratorClassString();
|
||||||
|
abstract public function getComponentClassString();
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_app = $this->getMock($this->getComponentClassString());
|
||||||
|
$decorator = $this->getDecoratorClassString();
|
||||||
|
$this->_serv = new $decorator($this->_app);
|
||||||
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
|
||||||
|
$this->doOpen($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doOpen($conn) {
|
||||||
|
$this->_serv->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isExpectedConnection() {
|
||||||
|
return new \PHPUnit_Framework_Constraint_IsInstanceOf($this->getConnectionClassString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOpen() {
|
||||||
|
$this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
|
||||||
|
$this->doOpen($this->getMock('\Ratchet\ConnectionInterface'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnClose() {
|
||||||
|
$this->_app->expects($this->once())->method('onClose')->with($this->isExpectedConnection());
|
||||||
|
$this->_serv->onClose($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnError() {
|
||||||
|
$e = new \Exception('Whoops!');
|
||||||
|
$this->_app->expects($this->once())->method('onError')->with($this->isExpectedConnection(), $e);
|
||||||
|
$this->_serv->onError($this->_conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passthroughMessageTest($value) {
|
||||||
|
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $value);
|
||||||
|
$this->_serv->onMessage($this->_conn, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php
vendored
Normal file
35
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Component.php
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class Component implements MessageComponentInterface, WsServerInterface {
|
||||||
|
public $last = array();
|
||||||
|
|
||||||
|
public $protocols = array();
|
||||||
|
|
||||||
|
public function __construct(ComponentInterface $app = null) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $from, $msg) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return $this->protocols;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php
vendored
Normal file
20
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/Connection.php
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class Connection implements ConnectionInterface {
|
||||||
|
public $last = array(
|
||||||
|
'send' => ''
|
||||||
|
, 'close' => false
|
||||||
|
);
|
||||||
|
|
||||||
|
public $remoteAddress = '127.0.0.1';
|
||||||
|
|
||||||
|
public function send($data) {
|
||||||
|
$this->last[__FUNCTION__] = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->last[__FUNCTION__] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php
vendored
Normal file
22
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/ConnectionDecorator.php
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\AbstractConnectionDecorator;
|
||||||
|
|
||||||
|
class ConnectionDecorator extends AbstractConnectionDecorator {
|
||||||
|
public $last = array(
|
||||||
|
'write' => ''
|
||||||
|
, 'end' => false
|
||||||
|
);
|
||||||
|
|
||||||
|
public function send($data) {
|
||||||
|
$this->last[__FUNCTION__] = $data;
|
||||||
|
|
||||||
|
$this->getConnection()->send($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
$this->last[__FUNCTION__] = true;
|
||||||
|
|
||||||
|
$this->getConnection()->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php
vendored
Normal file
43
vendor/cboden/ratchet/tests/helpers/Ratchet/Mock/WampComponent.php
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Mock;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
|
class WampComponent implements WampServerInterface, WsServerInterface {
|
||||||
|
public $last = array();
|
||||||
|
|
||||||
|
public $protocols = array();
|
||||||
|
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return $this->protocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onCall(ConnectionInterface $conn, $id, $procURI, array $params) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUnSubscribe(ConnectionInterface $conn, $topic) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOpen(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {
|
||||||
|
$this->last[__FUNCTION__] = func_get_args();
|
||||||
|
}
|
||||||
|
}
|
||||||
28
vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php
vendored
Normal file
28
vendor/cboden/ratchet/tests/helpers/Ratchet/NullComponent.php
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
use Ratchet\ConnectionInterface;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
|
||||||
|
class NullComponent implements MessageComponentInterface, WsServerInterface, WampServerInterface {
|
||||||
|
public function onOpen(ConnectionInterface $conn) {}
|
||||||
|
|
||||||
|
public function onMessage(ConnectionInterface $conn, $msg) {}
|
||||||
|
|
||||||
|
public function onClose(ConnectionInterface $conn) {}
|
||||||
|
|
||||||
|
public function onError(ConnectionInterface $conn, \Exception $e) {}
|
||||||
|
|
||||||
|
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {}
|
||||||
|
|
||||||
|
public function onSubscribe(ConnectionInterface $conn, $topic) {}
|
||||||
|
|
||||||
|
public function onUnSubscribe(ConnectionInterface $conn, $topic) {}
|
||||||
|
|
||||||
|
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude = array(), array $eligible = array()) {}
|
||||||
|
|
||||||
|
public function getSubProtocols() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
vendor/cboden/ratchet/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php
vendored
Normal file
7
vendor/cboden/ratchet/tests/helpers/Ratchet/Wamp/Stub/WsWampServerInterface.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp\Stub;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Ratchet\Wamp\WampServerInterface;
|
||||||
|
|
||||||
|
interface WsWampServerInterface extends WsServerInterface, WampServerInterface {
|
||||||
|
}
|
||||||
7
vendor/cboden/ratchet/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php
vendored
Normal file
7
vendor/cboden/ratchet/tests/helpers/Ratchet/WebSocket/Stub/WsMessageComponentInterface.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\WebSocket\Stub;
|
||||||
|
use Ratchet\MessageComponentInterface;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
|
||||||
|
interface WsMessageComponentInterface extends MessageComponentInterface, WsServerInterface {
|
||||||
|
}
|
||||||
147
vendor/cboden/ratchet/tests/unit/AbstractConnectionDecoratorTest.php
vendored
Normal file
147
vendor/cboden/ratchet/tests/unit/AbstractConnectionDecoratorTest.php
vendored
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet;
|
||||||
|
use Ratchet\Mock\ConnectionDecorator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\AbstractConnectionDecorator
|
||||||
|
* @covers Ratchet\ConnectionInterface
|
||||||
|
*/
|
||||||
|
class AbstractConnectionDecoratorTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $mock;
|
||||||
|
protected $l1;
|
||||||
|
protected $l2;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->l1 = new ConnectionDecorator($this->mock);
|
||||||
|
$this->l2 = new ConnectionDecorator($this->l1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGet() {
|
||||||
|
$var = 'hello';
|
||||||
|
$val = 'world';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->l1->$var);
|
||||||
|
$this->assertEquals($val, $this->l2->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSet() {
|
||||||
|
$var = 'Chris';
|
||||||
|
$val = 'Boden';
|
||||||
|
|
||||||
|
$this->l1->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->mock->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetLevel2() {
|
||||||
|
$var = 'Try';
|
||||||
|
$val = 'Again';
|
||||||
|
|
||||||
|
$this->l2->$var = $val;
|
||||||
|
|
||||||
|
$this->assertEquals($val, $this->mock->$var);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsSetTrue() {
|
||||||
|
$var = 'PHP';
|
||||||
|
$val = 'Ratchet';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
|
||||||
|
$this->assertTrue(isset($this->l1->$var));
|
||||||
|
$this->assertTrue(isset($this->l2->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsSetFalse() {
|
||||||
|
$var = 'herp';
|
||||||
|
$val = 'derp';
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->l1->$var));
|
||||||
|
$this->assertFalse(isset($this->l2->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnset() {
|
||||||
|
$var = 'Flying';
|
||||||
|
$val = 'Monkey';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
unset($this->l1->$var);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mock->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsetLevel2() {
|
||||||
|
$var = 'Flying';
|
||||||
|
$val = 'Monkey';
|
||||||
|
|
||||||
|
$this->mock->$var = $val;
|
||||||
|
unset($this->l2->$var);
|
||||||
|
|
||||||
|
$this->assertFalse(isset($this->mock->$var));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetConnection() {
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$conn = $method->invokeArgs($this->l1, array());
|
||||||
|
|
||||||
|
$this->assertSame($this->mock, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetConnectionLevel2() {
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\AbstractConnectionDecorator');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$conn = $method->invokeArgs($this->l2, array());
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWrapperCanStoreSelfInDecorator() {
|
||||||
|
$this->mock->decorator = $this->l1;
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $this->l2->decorator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorRecursion() {
|
||||||
|
$this->mock->decorator = new \stdClass;
|
||||||
|
$this->mock->decorator->conn = $this->l1;
|
||||||
|
|
||||||
|
$this->assertSame($this->l1, $this->mock->decorator->conn);
|
||||||
|
$this->assertSame($this->l1, $this->l1->decorator->conn);
|
||||||
|
$this->assertSame($this->l1, $this->l2->decorator->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorRecursionLevel2() {
|
||||||
|
$this->mock->decorator = new \stdClass;
|
||||||
|
$this->mock->decorator->conn = $this->l2;
|
||||||
|
|
||||||
|
$this->assertSame($this->l2, $this->mock->decorator->conn);
|
||||||
|
$this->assertSame($this->l2, $this->l1->decorator->conn);
|
||||||
|
$this->assertSame($this->l2, $this->l2->decorator->conn);
|
||||||
|
|
||||||
|
// just for fun
|
||||||
|
$this->assertSame($this->l2, $this->l2->decorator->conn->decorator->conn->decorator->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothing() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->mock->nonExistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothingLevel1() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->l1->nonExistant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWarningGettingNothingLevel2() {
|
||||||
|
$this->setExpectedException('PHPUnit_Framework_Error');
|
||||||
|
$var = $this->l2->nonExistant;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php
vendored
Normal file
50
vendor/cboden/ratchet/tests/unit/Http/HttpRequestParserTest.php
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\HttpRequestParser
|
||||||
|
*/
|
||||||
|
class HttpRequestParserTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $parser;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->parser = new HttpRequestParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headersProvider() {
|
||||||
|
return array(
|
||||||
|
array(false, "GET / HTTP/1.1\r\nHost: socketo.me\r\n")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n1")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie✖\r\n\r\n")
|
||||||
|
, array(true, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\nHixie\r\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider headersProvider
|
||||||
|
*/
|
||||||
|
public function testIsEom($expected, $message) {
|
||||||
|
$this->assertEquals($expected, $this->parser->isEom($message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBufferOverflowResponse() {
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
|
||||||
|
$this->parser->maxSize = 20;
|
||||||
|
|
||||||
|
$this->assertNull($this->parser->onMessage($conn, "GET / HTTP/1.1\r\n"));
|
||||||
|
|
||||||
|
$this->setExpectedException('OverflowException');
|
||||||
|
|
||||||
|
$this->parser->onMessage($conn, "Header-Is: Too Big");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnTypeIsRequest() {
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$return = $this->parser->onMessage($conn, "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n");
|
||||||
|
|
||||||
|
$this->assertInstanceOf('\Psr\Http\Message\RequestInterface', $return);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php
vendored
Normal file
64
vendor/cboden/ratchet/tests/unit/Http/HttpServerTest.php
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\HttpServer
|
||||||
|
*/
|
||||||
|
class HttpServerTest extends AbstractMessageComponentTestCase {
|
||||||
|
public function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->_conn->httpHeadersReceived = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\ConnectionInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServer';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOpen() {
|
||||||
|
$headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
|
||||||
|
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_app->expects($this->once())->method('onOpen')->with($this->isExpectedConnection());
|
||||||
|
$this->_serv->onMessage($this->_conn, $headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageAfterHeaders() {
|
||||||
|
$headers = "GET / HTTP/1.1\r\nHost: socketo.me\r\n\r\n";
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_serv->onMessage($this->_conn, $headers);
|
||||||
|
|
||||||
|
$message = "Hello World!";
|
||||||
|
$this->_app->expects($this->once())->method('onMessage')->with($this->isExpectedConnection(), $message);
|
||||||
|
$this->_serv->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBufferOverflow() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
|
||||||
|
$this->_serv->onMessage($this->_conn, str_repeat('a', 5000));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseIfNotEstablished() {
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_serv->onError($this->_conn, new \Exception('Whoops!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBufferHeaders() {
|
||||||
|
$this->_conn->httpHeadersReceived = false;
|
||||||
|
$this->_app->expects($this->never())->method('onOpen');
|
||||||
|
$this->_app->expects($this->never())->method('onMessage');
|
||||||
|
|
||||||
|
$this->_serv->onMessage($this->_conn, "GET / HTTP/1.1");
|
||||||
|
}
|
||||||
|
}
|
||||||
46
vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php
vendored
Normal file
46
vendor/cboden/ratchet/tests/unit/Http/OriginCheckTest.php
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\OriginCheck
|
||||||
|
*/
|
||||||
|
class OriginCheckTest extends AbstractMessageComponentTestCase {
|
||||||
|
protected $_reqStub;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_reqStub = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||||
|
$this->_reqStub->expects($this->any())->method('getHeader')->will($this->returnValue(['localhost']));
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->_serv->allowedOrigins[] = 'localhost';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doOpen($conn) {
|
||||||
|
$this->_serv->onOpen($conn, $this->_reqStub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\ConnectionInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return '\Ratchet\Http\OriginCheck';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Http\HttpServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseOnNonMatchingOrigin() {
|
||||||
|
$this->_serv->allowedOrigins = ['socketo.me'];
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$this->_serv->onOpen($this->_conn, $this->_reqStub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessage() {
|
||||||
|
$this->passthroughMessageTest('Hello World!');
|
||||||
|
}
|
||||||
|
}
|
||||||
165
vendor/cboden/ratchet/tests/unit/Http/RouterTest.php
vendored
Normal file
165
vendor/cboden/ratchet/tests/unit/Http/RouterTest.php
vendored
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Http;
|
||||||
|
use Ratchet\WebSocket\WsServerInterface;
|
||||||
|
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||||
|
use Symfony\Component\Routing\RequestContext;
|
||||||
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Http\Router
|
||||||
|
*/
|
||||||
|
class RouterTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_router;
|
||||||
|
protected $_matcher;
|
||||||
|
protected $_conn;
|
||||||
|
protected $_uri;
|
||||||
|
protected $_req;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_uri = $this->getMock('Psr\Http\Message\UriInterface');
|
||||||
|
$this->_req = $this->getMock('\Psr\Http\Message\RequestInterface');
|
||||||
|
$this->_req
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getUri')
|
||||||
|
->will($this->returnValue($this->_uri));
|
||||||
|
$this->_matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
|
||||||
|
$this->_matcher
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getContext')
|
||||||
|
->will($this->returnValue($this->getMock('Symfony\Component\Routing\RequestContext')));
|
||||||
|
$this->_router = new Router($this->_matcher);
|
||||||
|
|
||||||
|
$this->_uri->expects($this->any())->method('getPath')->will($this->returnValue('ws://doesnt.matter/'));
|
||||||
|
$this->_uri->expects($this->any())->method('withQuery')->with($this->callback(function($val) {
|
||||||
|
$this->setResult($val);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}))->will($this->returnSelf());
|
||||||
|
$this->_uri->expects($this->any())->method('getQuery')->will($this->returnCallback([$this, 'getResult']));
|
||||||
|
$this->_req->expects($this->any())->method('withUri')->will($this->returnSelf());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFourOhFour() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$nope = new ResourceNotFoundException;
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->throwException($nope));
|
||||||
|
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNullRequest() {
|
||||||
|
$this->setExpectedException('\UnexpectedValueException');
|
||||||
|
$this->_router->onOpen($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerIsMessageComponentInterface() {
|
||||||
|
$this->setExpectedException('\UnexpectedValueException');
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => new \StdClass)));
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnOpen() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
|
||||||
|
$expectedConn = new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\ConnectionInterface');
|
||||||
|
$controller->expects($this->once())->method('onOpen')->with($expectedConn, $this->_req);
|
||||||
|
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will($this->returnValue(array('_controller' => $controller)));
|
||||||
|
$this->_router->onOpen($this->_conn, $this->_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnMessageBubbles() {
|
||||||
|
$message = "The greatest trick the Devil ever pulled was convincing the world he didn't exist";
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$controller->expects($this->once())->method('onMessage')->with($this->_conn, $message);
|
||||||
|
|
||||||
|
$this->_conn->controller = $controller;
|
||||||
|
|
||||||
|
$this->_router->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnCloseBubbles() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$controller->expects($this->once())->method('onClose')->with($this->_conn);
|
||||||
|
|
||||||
|
$this->_conn->controller = $controller;
|
||||||
|
|
||||||
|
$this->_router->onClose($this->_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testControllerOnErrorBubbles() {
|
||||||
|
$e= new \Exception('One cannot be betrayed if one has no exceptions');
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$controller->expects($this->once())->method('onError')->with($this->_conn, $e);
|
||||||
|
|
||||||
|
$this->_conn->controller = $controller;
|
||||||
|
|
||||||
|
$this->_router->onError($this->_conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRouterGeneratesRouteParameters() {
|
||||||
|
/** @var $controller WsServerInterface */
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
/** @var $matcher UrlMatcherInterface */
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will(
|
||||||
|
$this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
|
||||||
|
);
|
||||||
|
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||||
|
|
||||||
|
$router = new Router($this->_matcher);
|
||||||
|
|
||||||
|
$router->onOpen($conn, $this->_req);
|
||||||
|
|
||||||
|
$this->assertEquals('foo=bar&baz=qux', $this->_req->getUri()->getQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQueryParams() {
|
||||||
|
$controller = $this->getMockBuilder('\Ratchet\WebSocket\WsServer')->disableOriginalConstructor()->getMock();
|
||||||
|
$this->_matcher->expects($this->any())->method('match')->will(
|
||||||
|
$this->returnValue(['_controller' => $controller, 'foo' => 'bar', 'baz' => 'qux'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$conn = $this->getMock('Ratchet\Mock\Connection');
|
||||||
|
$request = $this->getMock('Psr\Http\Message\RequestInterface');
|
||||||
|
$uri = new \GuzzleHttp\Psr7\Uri('ws://doesnt.matter/endpoint?hello=world&foo=nope');
|
||||||
|
|
||||||
|
$request->expects($this->any())->method('getUri')->will($this->returnCallback(function() use (&$uri) {
|
||||||
|
return $uri;
|
||||||
|
}));
|
||||||
|
$request->expects($this->any())->method('withUri')->with($this->callback(function($url) use (&$uri) {
|
||||||
|
$uri = $url;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}))->will($this->returnSelf());
|
||||||
|
|
||||||
|
$router = new Router($this->_matcher);
|
||||||
|
$router->onOpen($conn, $request);
|
||||||
|
|
||||||
|
$this->assertEquals('foo=nope&baz=qux&hello=world', $request->getUri()->getQuery());
|
||||||
|
$this->assertEquals('ws', $request->getUri()->getScheme());
|
||||||
|
$this->assertEquals('doesnt.matter', $request->getUri()->getHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testImpatientClientOverflow() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$header = "GET /nope HTTP/1.1
|
||||||
|
Upgrade: websocket
|
||||||
|
Connection: upgrade
|
||||||
|
Host: localhost
|
||||||
|
Origin: http://localhost
|
||||||
|
Sec-WebSocket-Version: 13\r\n\r\n";
|
||||||
|
|
||||||
|
$app = new HttpServer(new Router(new UrlMatcher(new RouteCollection, new RequestContext)));
|
||||||
|
$app->onOpen($this->_conn);
|
||||||
|
$app->onMessage($this->_conn, $header);
|
||||||
|
$app->onMessage($this->_conn, 'Silly body');
|
||||||
|
}
|
||||||
|
}
|
||||||
26
vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php
vendored
Normal file
26
vendor/cboden/ratchet/tests/unit/Server/EchoServerTest.php
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\Server\EchoServer;
|
||||||
|
|
||||||
|
class EchoServerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_conn;
|
||||||
|
protected $_comp;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_comp = new EchoServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageEchod() {
|
||||||
|
$message = 'Tillsonburg, my back still aches when I hear that word.';
|
||||||
|
$this->_conn->expects($this->once())->method('send')->with($message);
|
||||||
|
$this->_comp->onMessage($this->_conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testErrorClosesConnection() {
|
||||||
|
ob_start();
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_comp->onError($this->_conn, new \Exception);
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
}
|
||||||
152
vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php
vendored
Normal file
152
vendor/cboden/ratchet/tests/unit/Server/FlashPolicyComponentTest.php
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Application\Server;
|
||||||
|
use Ratchet\Server\FlashPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\FlashPolicy
|
||||||
|
*/
|
||||||
|
class FlashPolicyTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
protected $_policy;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_policy = new FlashPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPolicyRender() {
|
||||||
|
$this->_policy->setSiteControl('all');
|
||||||
|
$this->_policy->addAllowedAccess('example.com', '*');
|
||||||
|
$this->_policy->addAllowedAccess('dev.example.com', '*');
|
||||||
|
|
||||||
|
$this->assertInstanceOf('SimpleXMLElement', $this->_policy->renderPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidPolicyReader() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
$this->_policy->renderPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidDomainPolicyReader() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
$this->_policy->setSiteControl('all');
|
||||||
|
$this->_policy->addAllowedAccess('dev.example.*', '*');
|
||||||
|
$this->_policy->renderPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider siteControl
|
||||||
|
*/
|
||||||
|
public function testSiteControlValidation($accept, $permittedCrossDomainPolicies) {
|
||||||
|
$this->assertEquals($accept, $this->_policy->validateSiteControl($permittedCrossDomainPolicies));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function siteControl() {
|
||||||
|
return array(
|
||||||
|
array(true, 'all')
|
||||||
|
, array(true, 'none')
|
||||||
|
, array(true, 'master-only')
|
||||||
|
, array(false, 'by-content-type')
|
||||||
|
, array(false, 'by-ftp-filename')
|
||||||
|
, array(false, '')
|
||||||
|
, array(false, 'all ')
|
||||||
|
, array(false, 'asdf')
|
||||||
|
, array(false, '@893830')
|
||||||
|
, array(false, '*')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider URI
|
||||||
|
*/
|
||||||
|
public function testDomainValidation($accept, $domain) {
|
||||||
|
$this->assertEquals($accept, $this->_policy->validateDomain($domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function URI() {
|
||||||
|
return array(
|
||||||
|
array(true, '*')
|
||||||
|
, array(true, 'example.com')
|
||||||
|
, array(true, 'exam-ple.com')
|
||||||
|
, array(true, '*.example.com')
|
||||||
|
, array(true, 'www.example.com')
|
||||||
|
, array(true, 'dev.dev.example.com')
|
||||||
|
, array(true, 'http://example.com')
|
||||||
|
, array(true, 'https://example.com')
|
||||||
|
, array(true, 'http://*.example.com')
|
||||||
|
, array(false, 'exam*ple.com')
|
||||||
|
, array(true, '127.0.255.1')
|
||||||
|
, array(true, 'localhost')
|
||||||
|
, array(false, 'www.example.*')
|
||||||
|
, array(false, 'www.exa*le.com')
|
||||||
|
, array(false, 'www.example.*com')
|
||||||
|
, array(false, '*.example.*')
|
||||||
|
, array(false, 'gasldf*$#a0sdf0a8sdf')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider ports
|
||||||
|
*/
|
||||||
|
public function testPortValidation($accept, $ports) {
|
||||||
|
$this->assertEquals($accept, $this->_policy->validatePorts($ports));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ports() {
|
||||||
|
return array(
|
||||||
|
array(true, '*')
|
||||||
|
, array(true, '80')
|
||||||
|
, array(true, '80,443')
|
||||||
|
, array(true, '507,516-523')
|
||||||
|
, array(true, '507,516-523,333')
|
||||||
|
, array(true, '507,516-523,507,516-523')
|
||||||
|
, array(false, '516-')
|
||||||
|
, array(true, '516-523,11')
|
||||||
|
, array(false, '516,-523,11')
|
||||||
|
, array(false, 'example')
|
||||||
|
, array(false, 'asdf,123')
|
||||||
|
, array(false, '--')
|
||||||
|
, array(false, ',,,')
|
||||||
|
, array(false, '838*')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAllowedAccessOnlyAcceptsValidPorts() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
|
||||||
|
$this->_policy->addAllowedAccess('*', 'nope');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetSiteControlThrowsException() {
|
||||||
|
$this->setExpectedException('UnexpectedValueException');
|
||||||
|
|
||||||
|
$this->_policy->setSiteControl('nope');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testErrorClosesConnection() {
|
||||||
|
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$this->_policy->onError($conn, new \Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageSendsString() {
|
||||||
|
$this->_policy->addAllowedAccess('*', '*');
|
||||||
|
|
||||||
|
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn->expects($this->once())->method('send')->with($this->isType('string'));
|
||||||
|
|
||||||
|
$this->_policy->onMessage($conn, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpenExists() {
|
||||||
|
$this->assertTrue(method_exists($this->_policy, 'onOpen'));
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_policy->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCloseExists() {
|
||||||
|
$this->assertTrue(method_exists($this->_policy, 'onClose'));
|
||||||
|
$conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->_policy->onClose($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php
vendored
Normal file
32
vendor/cboden/ratchet/tests/unit/Server/IoConnectionTest.php
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Application\Server;
|
||||||
|
use Ratchet\Server\IoConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\IoConnection
|
||||||
|
*/
|
||||||
|
class IoConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $sock;
|
||||||
|
protected $conn;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->sock = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||||
|
$this->conn = new IoConnection($this->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCloseBubbles() {
|
||||||
|
$this->sock->expects($this->once())->method('end');
|
||||||
|
$this->conn->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendBubbles() {
|
||||||
|
$msg = '6 hour rides are productive';
|
||||||
|
|
||||||
|
$this->sock->expects($this->once())->method('write')->with($msg);
|
||||||
|
$this->conn->send($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendReturnsSelf() {
|
||||||
|
$this->assertSame($this->conn, $this->conn->send('fluent interface'));
|
||||||
|
}
|
||||||
|
}
|
||||||
127
vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php
vendored
Normal file
127
vendor/cboden/ratchet/tests/unit/Server/IoServerTest.php
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\Server\IoServer;
|
||||||
|
use React\EventLoop\StreamSelectLoop;
|
||||||
|
use React\EventLoop\LoopInterface;
|
||||||
|
use React\Socket\Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\IoServer
|
||||||
|
*/
|
||||||
|
class IoServerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
protected $app;
|
||||||
|
|
||||||
|
protected $port;
|
||||||
|
|
||||||
|
protected $reactor;
|
||||||
|
|
||||||
|
protected function tickLoop(LoopInterface $loop) {
|
||||||
|
$loop->futureTick(function () use ($loop) {
|
||||||
|
$loop->stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
$loop->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->app = $this->getMock('\\Ratchet\\MessageComponentInterface');
|
||||||
|
|
||||||
|
$loop = new StreamSelectLoop;
|
||||||
|
$this->reactor = new Server(0, $loop);
|
||||||
|
|
||||||
|
$uri = $this->reactor->getAddress();
|
||||||
|
$this->port = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri, PHP_URL_PORT);
|
||||||
|
$this->server = new IoServer($this->app, $this->reactor, $loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpen() {
|
||||||
|
$this->app->expects($this->once())->method('onOpen')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
|
||||||
|
|
||||||
|
$client = stream_socket_client("tcp://localhost:{$this->port}");
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
//$this->assertTrue(is_string($this->app->last['onOpen'][0]->remoteAddress));
|
||||||
|
//$this->assertTrue(is_int($this->app->last['onOpen'][0]->resourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnData() {
|
||||||
|
$msg = 'Hello World!';
|
||||||
|
|
||||||
|
$this->app->expects($this->once())->method('onMessage')->with(
|
||||||
|
$this->isInstanceOf('\\Ratchet\\ConnectionInterface')
|
||||||
|
, $msg
|
||||||
|
);
|
||||||
|
|
||||||
|
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
|
||||||
|
socket_set_block($client);
|
||||||
|
socket_connect($client, 'localhost', $this->port);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
socket_write($client, $msg);
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
socket_shutdown($client, 1);
|
||||||
|
socket_shutdown($client, 0);
|
||||||
|
socket_close($client);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnClose() {
|
||||||
|
$this->app->expects($this->once())->method('onClose')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'));
|
||||||
|
|
||||||
|
$client = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||||
|
socket_set_option($client, SOL_SOCKET, SO_SNDBUF, 4096);
|
||||||
|
socket_set_block($client);
|
||||||
|
socket_connect($client, 'localhost', $this->port);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
|
||||||
|
socket_shutdown($client, 1);
|
||||||
|
socket_shutdown($client, 0);
|
||||||
|
socket_close($client);
|
||||||
|
|
||||||
|
$this->tickLoop($this->server->loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFactory() {
|
||||||
|
$this->assertInstanceOf('\\Ratchet\\Server\\IoServer', IoServer::factory($this->app, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoLoopProvidedError() {
|
||||||
|
$this->setExpectedException('RuntimeException');
|
||||||
|
|
||||||
|
$io = new IoServer($this->app, $this->reactor);
|
||||||
|
$io->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnErrorPassesException() {
|
||||||
|
$conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||||
|
$conn->decor = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$err = new \Exception("Nope");
|
||||||
|
|
||||||
|
$this->app->expects($this->once())->method('onError')->with($conn->decor, $err);
|
||||||
|
|
||||||
|
$this->server->handleError($err, $conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onErrorCalledWhenExceptionThrown() {
|
||||||
|
$this->markTestIncomplete("Need to learn how to throw an exception from a mock");
|
||||||
|
|
||||||
|
$conn = $this->getMock('\\React\\Socket\\ConnectionInterface');
|
||||||
|
$this->server->handleConnect($conn);
|
||||||
|
|
||||||
|
$e = new \Exception;
|
||||||
|
$this->app->expects($this->once())->method('onMessage')->with($this->isInstanceOf('\\Ratchet\\ConnectionInterface'), 'f')->will($e);
|
||||||
|
$this->app->expects($this->once())->method('onError')->with($this->instanceOf('\\Ratchet\\ConnectionInterface', $e));
|
||||||
|
|
||||||
|
$this->server->handleData('f', $conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
125
vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php
vendored
Normal file
125
vendor/cboden/ratchet/tests/unit/Server/IpBlackListComponentTest.php
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Server;
|
||||||
|
use Ratchet\Server\IpBlackList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Server\IpBlackList
|
||||||
|
*/
|
||||||
|
class IpBlackListTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $blocker;
|
||||||
|
protected $mock;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = $this->getMock('\\Ratchet\\MessageComponentInterface');
|
||||||
|
$this->blocker = new IpBlackList($this->mock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpen() {
|
||||||
|
$this->mock->expects($this->exactly(3))->method('onOpen');
|
||||||
|
|
||||||
|
$conn1 = $this->newConn();
|
||||||
|
$conn2 = $this->newConn();
|
||||||
|
$conn3 = $this->newConn();
|
||||||
|
|
||||||
|
$this->blocker->onOpen($conn1);
|
||||||
|
$this->blocker->onOpen($conn3);
|
||||||
|
$this->blocker->onOpen($conn2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockDoesNotTriggerOnOpen() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->blocker->blockAddress($conn->remoteAddress);
|
||||||
|
|
||||||
|
$this->mock->expects($this->never())->method('onOpen');
|
||||||
|
|
||||||
|
$ret = $this->blocker->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockDoesNotTriggerOnClose() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->blocker->blockAddress($conn->remoteAddress);
|
||||||
|
|
||||||
|
$this->mock->expects($this->never())->method('onClose');
|
||||||
|
|
||||||
|
$ret = $this->blocker->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageDecoration() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$msg = 'Hello not being blocked';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onMessage')->with($conn, $msg);
|
||||||
|
|
||||||
|
$this->blocker->onMessage($conn, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCloseDecoration() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onClose')->with($conn);
|
||||||
|
|
||||||
|
$this->blocker->onClose($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockClosesConnection() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$this->blocker->blockAddress($conn->remoteAddress);
|
||||||
|
|
||||||
|
$conn->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$this->blocker->onOpen($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAndRemoveWithFluentInterfaces() {
|
||||||
|
$blockOne = '127.0.0.1';
|
||||||
|
$blockTwo = '192.168.1.1';
|
||||||
|
$unblock = '75.119.207.140';
|
||||||
|
|
||||||
|
$this->blocker
|
||||||
|
->blockAddress($unblock)
|
||||||
|
->blockAddress($blockOne)
|
||||||
|
->unblockAddress($unblock)
|
||||||
|
->blockAddress($blockTwo)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->assertEquals(array($blockOne, $blockTwo), $this->blocker->getBlockedAddresses());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDecoratorPassesErrors() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$e = new \Exception('I threw an error');
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onError')->with($conn, $e);
|
||||||
|
|
||||||
|
$this->blocker->onError($conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addressProvider() {
|
||||||
|
return array(
|
||||||
|
array('127.0.0.1', '127.0.0.1')
|
||||||
|
, array('localhost', 'localhost')
|
||||||
|
, array('fe80::1%lo0', 'fe80::1%lo0')
|
||||||
|
, array('127.0.0.1', '127.0.0.1:6392')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider addressProvider
|
||||||
|
*/
|
||||||
|
public function testFilterAddress($expected, $input) {
|
||||||
|
$this->assertEquals($expected, $this->blocker->filterAddress($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnblockingSilentlyFails() {
|
||||||
|
$this->assertInstanceOf('\\Ratchet\\Server\\IpBlackList', $this->blocker->unblockAddress('localhost'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
$conn = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn->remoteAddress = '127.0.0.1';
|
||||||
|
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php
vendored
Normal file
43
vendor/cboden/ratchet/tests/unit/Session/Serialize/PhpHandlerTest.php
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Serialize;
|
||||||
|
use Ratchet\Session\Serialize\PhpHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Session\Serialize\PhpHandler
|
||||||
|
*/
|
||||||
|
class PhpHandlerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_handler;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_handler = new PhpHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function serializedProvider() {
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'_sf2_attributes|a:2:{s:5:"hello";s:5:"world";s:4:"last";i:1332872102;}_sf2_flashes|a:0:{}'
|
||||||
|
, array(
|
||||||
|
'_sf2_attributes' => array(
|
||||||
|
'hello' => 'world'
|
||||||
|
, 'last' => 1332872102
|
||||||
|
)
|
||||||
|
, '_sf2_flashes' => array()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider serializedProvider
|
||||||
|
*/
|
||||||
|
public function testUnserialize($in, $expected) {
|
||||||
|
$this->assertEquals($expected, $this->_handler->unserialize($in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider serializedProvider
|
||||||
|
*/
|
||||||
|
public function testSerialize($serialized, $original) {
|
||||||
|
$this->assertEquals($serialized, $this->_handler->serialize($original));
|
||||||
|
}
|
||||||
|
}
|
||||||
126
vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php
vendored
Normal file
126
vendor/cboden/ratchet/tests/unit/Session/SessionComponentTest.php
vendored
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?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() {
|
||||||
|
return $this->markTestIncomplete('Test needs to be updated for ini_set issue in PHP 7.2');
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php
vendored
Normal file
53
vendor/cboden/ratchet/tests/unit/Session/Storage/VirtualSessionStoragePDOTest.php
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Session\Storage;
|
||||||
|
use Ratchet\Session\Serialize\PhpHandler;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
|
||||||
|
|
||||||
|
class VirtualSessionStoragePDOTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
/**
|
||||||
|
* @var VirtualSessionStorage
|
||||||
|
*/
|
||||||
|
protected $_virtualSessionStorage;
|
||||||
|
|
||||||
|
protected $_pathToDB;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
|
||||||
|
return $this->markTestSkipped('Session test requires PDO and pdo_sqlite');
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema = <<<SQL
|
||||||
|
CREATE TABLE `sessions` (
|
||||||
|
`sess_id` VARBINARY(128) NOT NULL PRIMARY KEY,
|
||||||
|
`sess_data` BLOB NOT NULL,
|
||||||
|
`sess_time` INTEGER UNSIGNED NOT NULL,
|
||||||
|
`sess_lifetime` MEDIUMINT NOT NULL
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
$this->_pathToDB = tempnam(sys_get_temp_dir(), 'SQ3');;
|
||||||
|
$dsn = 'sqlite:' . $this->_pathToDB;
|
||||||
|
|
||||||
|
$pdo = new \PDO($dsn);
|
||||||
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->exec($schema);
|
||||||
|
$pdo = null;
|
||||||
|
|
||||||
|
$sessionHandler = new PdoSessionHandler($dsn);
|
||||||
|
$serializer = new PhpHandler();
|
||||||
|
$this->_virtualSessionStorage = new VirtualSessionStorage($sessionHandler, 'foobar', $serializer);
|
||||||
|
$this->_virtualSessionStorage->registerBag(new FlashBag());
|
||||||
|
$this->_virtualSessionStorage->registerBag(new AttributeBag());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
unlink($this->_pathToDB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStartWithDSN() {
|
||||||
|
$this->_virtualSessionStorage->start();
|
||||||
|
|
||||||
|
$this->assertTrue($this->_virtualSessionStorage->isStarted());
|
||||||
|
}
|
||||||
|
}
|
||||||
295
vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php
vendored
Normal file
295
vendor/cboden/ratchet/tests/unit/Wamp/ServerProtocolTest.php
vendored
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\Mock\Connection;
|
||||||
|
use Ratchet\Mock\WampComponent as TestComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Ratchet\Wamp\ServerProtocol
|
||||||
|
* @covers \Ratchet\Wamp\WampServerInterface
|
||||||
|
* @covers \Ratchet\Wamp\WampConnection
|
||||||
|
*/
|
||||||
|
class ServerProtocolTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $_comp;
|
||||||
|
|
||||||
|
protected $_app;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->_app = new TestComponent;
|
||||||
|
$this->_comp = new ServerProtocol($this->_app);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
return new Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invalidMessageProvider() {
|
||||||
|
return [
|
||||||
|
[0]
|
||||||
|
, [3]
|
||||||
|
, [4]
|
||||||
|
, [8]
|
||||||
|
, [9]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider invalidMessageProvider
|
||||||
|
*/
|
||||||
|
public function testInvalidMessages($type) {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode([$type]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWelcomeMessage() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$message = $conn->last['send'];
|
||||||
|
$json = json_decode($message);
|
||||||
|
|
||||||
|
$this->assertEquals(4, count($json));
|
||||||
|
$this->assertEquals(0, $json[0]);
|
||||||
|
$this->assertTrue(is_string($json[1]));
|
||||||
|
$this->assertEquals(1, $json[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubscribe() {
|
||||||
|
$uri = 'http://example.com';
|
||||||
|
$clientMessage = array(5, $uri);
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $this->_app->last['onSubscribe'][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnSubscribe() {
|
||||||
|
$uri = 'http://example.com/endpoint';
|
||||||
|
$clientMessage = array(6, $uri);
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $this->_app->last['onUnSubscribe'][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callProvider() {
|
||||||
|
return [
|
||||||
|
[2, 'a', 'b']
|
||||||
|
, [2, ['a', 'b']]
|
||||||
|
, [1, 'one']
|
||||||
|
, [3, 'one', 'two', 'three']
|
||||||
|
, [3, ['un', 'deux', 'trois']]
|
||||||
|
, [2, 'hi', ['hello', 'world']]
|
||||||
|
, [2, ['hello', 'world'], 'hi']
|
||||||
|
, [2, ['hello' => 'world', 'herp' => 'derp']]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider callProvider
|
||||||
|
*/
|
||||||
|
public function testCall() {
|
||||||
|
$args = func_get_args();
|
||||||
|
$paramNum = array_shift($args);
|
||||||
|
|
||||||
|
$uri = 'http://example.com/endpoint/' . rand(1, 100);
|
||||||
|
$id = uniqid('', false);
|
||||||
|
$clientMessage = array_merge(array(2, $id, $uri), $args);
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($id, $this->_app->last['onCall'][1]);
|
||||||
|
$this->assertEquals($uri, $this->_app->last['onCall'][2]);
|
||||||
|
|
||||||
|
$this->assertEquals($paramNum, count($this->_app->last['onCall'][3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublish() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$topic = 'pubsubhubbub';
|
||||||
|
$event = 'Here I am, publishing data';
|
||||||
|
|
||||||
|
$clientMessage = array(7, $topic, $event);
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode($clientMessage));
|
||||||
|
|
||||||
|
$this->assertEquals($topic, $this->_app->last['onPublish'][1]);
|
||||||
|
$this->assertEquals($event, $this->_app->last['onPublish'][2]);
|
||||||
|
$this->assertEquals(array(), $this->_app->last['onPublish'][3]);
|
||||||
|
$this->assertEquals(array(), $this->_app->last['onPublish'][4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublishAndExcludeMe() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', true)));
|
||||||
|
|
||||||
|
$this->assertEquals($conn->WAMP->sessionId, $this->_app->last['onPublish'][3][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPublishAndEligible() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
|
||||||
|
$buddy = uniqid('', false);
|
||||||
|
$friend = uniqid('', false);
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array(7, 'topic', 'event', false, array($buddy, $friend))));
|
||||||
|
|
||||||
|
$this->assertEquals(array(), $this->_app->last['onPublish'][3]);
|
||||||
|
$this->assertEquals(2, count($this->_app->last['onPublish'][4]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eventProvider() {
|
||||||
|
return array(
|
||||||
|
array('http://example.com', array('one', 'two'))
|
||||||
|
, array('curie', array(array('hello' => 'world', 'herp' => 'derp')))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider eventProvider
|
||||||
|
*/
|
||||||
|
public function testEvent($topic, $payload) {
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$conn->event($topic, $payload);
|
||||||
|
|
||||||
|
$eventString = $conn->last['send'];
|
||||||
|
|
||||||
|
$this->assertSame(array(8, $topic, $payload), json_decode($eventString, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnClosePropagation() {
|
||||||
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onClose($conn);
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$check = $method->invokeArgs($this->_app->last['onClose'][0], array());
|
||||||
|
|
||||||
|
$this->assertSame($conn, $check);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnErrorPropagation() {
|
||||||
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$e = new \Exception('Nope');
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onError($conn, $e);
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('\\Ratchet\\Wamp\\WampConnection');
|
||||||
|
$method = $class->getMethod('getConnection');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$check = $method->invokeArgs($this->_app->last['onError'][0], array());
|
||||||
|
|
||||||
|
$this->assertSame($conn, $check);
|
||||||
|
$this->assertSame($e, $this->_app->last['onError'][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrefix() {
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$prefix = 'incoming';
|
||||||
|
$fullURI = "http://example.com/$prefix";
|
||||||
|
$method = 'call';
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode(array(1, $prefix, $fullURI)));
|
||||||
|
|
||||||
|
$this->assertEquals($fullURI, $conn->WAMP->prefixes[$prefix]);
|
||||||
|
$this->assertEquals("$fullURI#$method", $conn->getUri("$prefix:$method"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMessageMustBeJson() {
|
||||||
|
$this->setExpectedException('\\Ratchet\\Wamp\\JsonException');
|
||||||
|
|
||||||
|
$conn = new Connection;
|
||||||
|
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, 'Hello World!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsReturnsArray() {
|
||||||
|
$this->assertTrue(is_array($this->_comp->getSubProtocols()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsGetFromApp() {
|
||||||
|
$this->_app->protocols = array('hello', 'world');
|
||||||
|
|
||||||
|
$this->assertGreaterThanOrEqual(3, count($this->_comp->getSubProtocols()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWampOnMessageApp() {
|
||||||
|
$app = $this->getMock('\\Ratchet\\Wamp\\WampServerInterface');
|
||||||
|
$wamp = new ServerProtocol($app);
|
||||||
|
|
||||||
|
$this->assertContains('wamp', $wamp->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function badFormatProvider() {
|
||||||
|
return array(
|
||||||
|
array(json_encode(true))
|
||||||
|
, array('{"valid":"json", "invalid": "message"}')
|
||||||
|
, array('{"0": "fail", "hello": "world"}')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider badFormatProvider
|
||||||
|
*/
|
||||||
|
public function testValidJsonButInvalidProtocol($message) {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
$this->_comp->onMessage($conn, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadClientInputFromNonStringTopic() {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode([5, ['hells', 'nope']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadPrefixWithNonStringTopic() {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode([1, ['hells', 'nope'], ['bad', 'input']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadPublishWithNonStringTopic() {
|
||||||
|
$this->setExpectedException('\Ratchet\Wamp\Exception');
|
||||||
|
|
||||||
|
$conn = new WampConnection($this->newConn());
|
||||||
|
$this->_comp->onOpen($conn);
|
||||||
|
|
||||||
|
$this->_comp->onMessage($conn, json_encode([7, ['bad', 'input'], 'Hider']));
|
||||||
|
}
|
||||||
|
}
|
||||||
226
vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php
vendored
Normal file
226
vendor/cboden/ratchet/tests/unit/Wamp/TopicManagerTest.php
vendored
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\TopicManager
|
||||||
|
*/
|
||||||
|
class TopicManagerTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
private $mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\Wamp\TopicManager
|
||||||
|
*/
|
||||||
|
private $mngr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Ratchet\ConnectionInterface
|
||||||
|
*/
|
||||||
|
private $conn;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->conn = $this->getMock('\Ratchet\ConnectionInterface');
|
||||||
|
$this->mock = $this->getMock('\Ratchet\Wamp\WampServerInterface');
|
||||||
|
$this->mngr = new TopicManager($this->mock);
|
||||||
|
|
||||||
|
$this->conn->WAMP = new \StdClass;
|
||||||
|
$this->mngr->onOpen($this->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTopicReturnsTopicObject() {
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array('The Topic'));
|
||||||
|
|
||||||
|
$this->assertInstanceOf('Ratchet\Wamp\Topic', $topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTopicCreatesTopicWithSameName() {
|
||||||
|
$name = 'The Topic';
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
$this->assertEquals($name, $topic->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetTopicReturnsSameObject() {
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array('No copy'));
|
||||||
|
$again = $method->invokeArgs($this->mngr, array('No copy'));
|
||||||
|
|
||||||
|
$this->assertSame($topic, $again);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnOpen() {
|
||||||
|
$this->mock->expects($this->once())->method('onOpen');
|
||||||
|
$this->mngr->onOpen($this->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCall() {
|
||||||
|
$id = uniqid();
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onCall')->with(
|
||||||
|
$this->conn
|
||||||
|
, $id
|
||||||
|
, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
, array()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onCall($this->conn, $id, 'new topic', array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnSubscribeCreatesTopicObject() {
|
||||||
|
$this->mock->expects($this->once())->method('onSubscribe')->with(
|
||||||
|
$this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, 'new topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTopicIsInConnectionOnSubscribe() {
|
||||||
|
$name = 'New Topic';
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
|
||||||
|
$this->assertTrue($this->conn->WAMP->subscriptions->contains($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoubleSubscriptionFiresOnce() {
|
||||||
|
$this->mock->expects($this->exactly(1))->method('onSubscribe');
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, 'same topic');
|
||||||
|
$this->mngr->onSubscribe($this->conn, 'same topic');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeEvent() {
|
||||||
|
$name = 'in and out';
|
||||||
|
$this->mock->expects($this->once())->method('onUnsubscribe')->with(
|
||||||
|
$this->conn, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeFiresOnce() {
|
||||||
|
$name = 'getting sleepy';
|
||||||
|
$this->mock->expects($this->exactly(1))->method('onUnsubscribe');
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnsubscribeRemovesTopicFromConnection() {
|
||||||
|
$name = 'Bye Bye Topic';
|
||||||
|
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onUnsubscribe($this->conn, $name);
|
||||||
|
|
||||||
|
$this->assertFalse($this->conn->WAMP->subscriptions->contains($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnPublishBubbles() {
|
||||||
|
$msg = 'Cover all the code!';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('onPublish')->with(
|
||||||
|
$this->conn
|
||||||
|
, $this->isInstanceOf('Ratchet\Wamp\Topic')
|
||||||
|
, $msg
|
||||||
|
, $this->isType('array')
|
||||||
|
, $this->isType('array')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mngr->onPublish($this->conn, 'topic coverage', $msg, array(), array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnCloseBubbles() {
|
||||||
|
$this->mock->expects($this->once())->method('onClose')->with($this->conn);
|
||||||
|
$this->mngr->onClose($this->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function topicProvider($name) {
|
||||||
|
$class = new \ReflectionClass('Ratchet\Wamp\TopicManager');
|
||||||
|
$method = $class->getMethod('getTopic');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$attribute = $class->getProperty('topicLookup');
|
||||||
|
$attribute->setAccessible(true);
|
||||||
|
|
||||||
|
$topic = $method->invokeArgs($this->mngr, array($name));
|
||||||
|
|
||||||
|
return array($topic, $attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnIsRemovedFromTopicOnClose() {
|
||||||
|
$name = 'State Testing';
|
||||||
|
list($topic, $attribute) = $this->topicProvider($name);
|
||||||
|
|
||||||
|
$this->assertCount(1, $attribute->getValue($this->mngr));
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $name);
|
||||||
|
$this->mngr->onClose($this->conn);
|
||||||
|
|
||||||
|
$this->assertFalse($topic->has($this->conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function topicConnExpectationProvider() {
|
||||||
|
return [
|
||||||
|
[ 'onClose', 0]
|
||||||
|
, ['onUnsubscribe', 0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider topicConnExpectationProvider
|
||||||
|
*/
|
||||||
|
public function testTopicRetentionFromLeavingConnections($methodCall, $expectation) {
|
||||||
|
$topicName = 'checkTopic';
|
||||||
|
list($topic, $attribute) = $this->topicProvider($topicName);
|
||||||
|
|
||||||
|
$this->mngr->onSubscribe($this->conn, $topicName);
|
||||||
|
call_user_func_array(array($this->mngr, $methodCall), array($this->conn, $topicName));
|
||||||
|
|
||||||
|
$this->assertCount($expectation, $attribute->getValue($this->mngr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnErrorBubbles() {
|
||||||
|
$e = new \Exception('All work and no play makes Chris a dull boy');
|
||||||
|
$this->mock->expects($this->once())->method('onError')->with($this->conn, $e);
|
||||||
|
|
||||||
|
$this->mngr->onError($this->conn, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsReturnsArray() {
|
||||||
|
$this->assertInternalType('array', $this->mngr->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocolsBubbles() {
|
||||||
|
$subs = array('hello', 'world');
|
||||||
|
$app = $this->getMock('Ratchet\Wamp\Stub\WsWampServerInterface');
|
||||||
|
$app->expects($this->once())->method('getSubProtocols')->will($this->returnValue($subs));
|
||||||
|
$mngr = new TopicManager($app);
|
||||||
|
|
||||||
|
$this->assertEquals($subs, $mngr->getSubProtocols());
|
||||||
|
}
|
||||||
|
}
|
||||||
164
vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php
vendored
Normal file
164
vendor/cboden/ratchet/tests/unit/Wamp/TopicTest.php
vendored
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\Topic
|
||||||
|
*/
|
||||||
|
class TopicTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
public function testGetId() {
|
||||||
|
$id = uniqid();
|
||||||
|
$topic = new Topic($id);
|
||||||
|
|
||||||
|
$this->assertEquals($id, $topic->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddAndCount() {
|
||||||
|
$topic = new Topic('merp');
|
||||||
|
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
|
||||||
|
$this->assertEquals(3, count($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemove() {
|
||||||
|
$topic = new Topic('boop');
|
||||||
|
$tracked = $this->newConn();
|
||||||
|
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
$topic->add($tracked);
|
||||||
|
$topic->add($this->newConn());
|
||||||
|
|
||||||
|
$topic->remove($tracked);
|
||||||
|
|
||||||
|
$this->assertEquals(2, count($topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBroadcast() {
|
||||||
|
$msg = 'Hello World!';
|
||||||
|
$name = 'Batman';
|
||||||
|
$protocol = json_encode(array(8, $name, $msg));
|
||||||
|
|
||||||
|
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
|
||||||
|
$first->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$second->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$topic = new Topic($name);
|
||||||
|
$topic->add($first);
|
||||||
|
$topic->add($second);
|
||||||
|
|
||||||
|
$topic->broadcast($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBroadcastWithExclude() {
|
||||||
|
$msg = 'Hello odd numbers';
|
||||||
|
$name = 'Excluding';
|
||||||
|
$protocol = json_encode(array(8, $name, $msg));
|
||||||
|
|
||||||
|
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
|
||||||
|
$first->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$second->expects($this->never())->method('send');
|
||||||
|
|
||||||
|
$third->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$topic = new Topic($name);
|
||||||
|
$topic->add($first);
|
||||||
|
$topic->add($second);
|
||||||
|
$topic->add($third);
|
||||||
|
|
||||||
|
$topic->broadcast($msg, array($second->WAMP->sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBroadcastWithEligible() {
|
||||||
|
$msg = 'Hello white list';
|
||||||
|
$name = 'Eligible';
|
||||||
|
$protocol = json_encode(array(8, $name, $msg));
|
||||||
|
|
||||||
|
$first = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$second = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
$third = $this->getMock('Ratchet\\Wamp\\WampConnection', array('send'), array($this->getMock('\\Ratchet\\ConnectionInterface')));
|
||||||
|
|
||||||
|
$first->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$second->expects($this->never())->method('send');
|
||||||
|
|
||||||
|
$third->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->with($this->equalTo($protocol));
|
||||||
|
|
||||||
|
$topic = new Topic($name);
|
||||||
|
$topic->add($first);
|
||||||
|
$topic->add($second);
|
||||||
|
$topic->add($third);
|
||||||
|
|
||||||
|
$topic->broadcast($msg, array(), array($first->WAMP->sessionId, $third->WAMP->sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIterator() {
|
||||||
|
$first = $this->newConn();
|
||||||
|
$second = $this->newConn();
|
||||||
|
$third = $this->newConn();
|
||||||
|
|
||||||
|
$topic = new Topic('Joker');
|
||||||
|
$topic->add($first)->add($second)->add($third);
|
||||||
|
|
||||||
|
$check = array($first, $second, $third);
|
||||||
|
|
||||||
|
foreach ($topic as $mock) {
|
||||||
|
$this->assertNotSame(false, array_search($mock, $check));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testToString() {
|
||||||
|
$name = 'Bane';
|
||||||
|
$topic = new Topic($name);
|
||||||
|
|
||||||
|
$this->assertEquals($name, (string)$topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesHave() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$topic = new Topic('Two Face');
|
||||||
|
$topic->add($conn);
|
||||||
|
|
||||||
|
$this->assertTrue($topic->has($conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesNotHave() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$topic = new Topic('Alfred');
|
||||||
|
|
||||||
|
$this->assertFalse($topic->has($conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDoesNotHaveAfterRemove() {
|
||||||
|
$conn = $this->newConn();
|
||||||
|
$topic = new Topic('Ras');
|
||||||
|
|
||||||
|
$topic->add($conn)->remove($conn);
|
||||||
|
|
||||||
|
$this->assertFalse($topic->has($conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newConn() {
|
||||||
|
return new WampConnection($this->getMock('\\Ratchet\\ConnectionInterface'));
|
||||||
|
}
|
||||||
|
}
|
||||||
77
vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php
vendored
Normal file
77
vendor/cboden/ratchet/tests/unit/Wamp/WampConnectionTest.php
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\WampConnection
|
||||||
|
*/
|
||||||
|
class WampConnectionTest extends \PHPUnit_Framework_TestCase {
|
||||||
|
protected $conn;
|
||||||
|
protected $mock;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->mock = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$this->conn = new WampConnection($this->mock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallResult() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$data = array('hello' => 'world', 'herp' => 'derp');
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(3, $callId, $data)));
|
||||||
|
|
||||||
|
$this->conn->callResult($callId, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallError() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
|
||||||
|
|
||||||
|
$this->conn->callError($callId, $uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCallErrorWithTopic() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, '')));
|
||||||
|
|
||||||
|
$this->conn->callError($callId, new Topic($uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetailedCallError() {
|
||||||
|
$callId = uniqid();
|
||||||
|
$uri = 'http://example.com/end/point';
|
||||||
|
$desc = 'beep boop beep';
|
||||||
|
$detail = 'Error: Too much awesome';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(4, $callId, $uri, $desc, $detail)));
|
||||||
|
|
||||||
|
$this->conn->callError($callId, $uri, $desc, $detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrefix() {
|
||||||
|
$shortOut = 'outgoing';
|
||||||
|
$longOut = 'http://example.com/outgoing';
|
||||||
|
|
||||||
|
$this->mock->expects($this->once())->method('send')->with(json_encode(array(1, $shortOut, $longOut)));
|
||||||
|
|
||||||
|
$this->conn->prefix($shortOut, $longOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetUriWhenNoCurieGiven() {
|
||||||
|
$uri = 'http://example.com/noshort';
|
||||||
|
|
||||||
|
$this->assertEquals($uri, $this->conn->getUri($uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClose() {
|
||||||
|
$mock = $this->getMock('\\Ratchet\\ConnectionInterface');
|
||||||
|
$conn = new WampConnection($mock);
|
||||||
|
|
||||||
|
$mock->expects($this->once())->method('close');
|
||||||
|
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
49
vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php
vendored
Normal file
49
vendor/cboden/ratchet/tests/unit/Wamp/WampServerTest.php
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Wamp;
|
||||||
|
use Ratchet\AbstractMessageComponentTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers Ratchet\Wamp\WampServer
|
||||||
|
*/
|
||||||
|
class WampServerTest extends AbstractMessageComponentTestCase {
|
||||||
|
public function getConnectionClassString() {
|
||||||
|
return '\Ratchet\Wamp\WampConnection';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDecoratorClassString() {
|
||||||
|
return 'Ratchet\Wamp\WampServer';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentClassString() {
|
||||||
|
return '\Ratchet\Wamp\WampServerInterface';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOnMessageToEvent() {
|
||||||
|
$published = 'Client published this message';
|
||||||
|
|
||||||
|
$this->_app->expects($this->once())->method('onPublish')->with(
|
||||||
|
$this->isExpectedConnection()
|
||||||
|
, new \PHPUnit_Framework_Constraint_IsInstanceOf('\Ratchet\Wamp\Topic')
|
||||||
|
, $published
|
||||||
|
, array()
|
||||||
|
, array()
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->_serv->onMessage($this->_conn, json_encode(array(7, 'topic', $published)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSubProtocols() {
|
||||||
|
// todo: could expand on this
|
||||||
|
$this->assertInternalType('array', $this->_serv->getSubProtocols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnectionClosesOnInvalidJson() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_serv->onMessage($this->_conn, 'invalid json');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConnectionClosesOnProtocolError() {
|
||||||
|
$this->_conn->expects($this->once())->method('close');
|
||||||
|
$this->_serv->onMessage($this->_conn, json_encode(array('valid' => 'json', 'invalid' => 'protocol')));
|
||||||
|
}
|
||||||
|
}
|
||||||
579
vendor/composer/ClassLoader.php
vendored
Normal file
579
vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see https://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see https://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
/** @var \Closure(string):void */
|
||||||
|
private static $includeFile;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $vendorDir;
|
||||||
|
|
||||||
|
// PSR-4
|
||||||
|
/**
|
||||||
|
* @var array<string, array<string, int>>
|
||||||
|
*/
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var array<string, list<string>>
|
||||||
|
*/
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
/**
|
||||||
|
* List of PSR-0 prefixes
|
||||||
|
*
|
||||||
|
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||||
|
*
|
||||||
|
* @var array<string, array<string, list<string>>>
|
||||||
|
*/
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
/**
|
||||||
|
* @var list<string>
|
||||||
|
*/
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $useIncludePath = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, bool>
|
||||||
|
*/
|
||||||
|
private $missingClasses = array();
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
private $apcuPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, self>
|
||||||
|
*/
|
||||||
|
private static $registeredLoaders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $vendorDir
|
||||||
|
*/
|
||||||
|
public function __construct($vendorDir = null)
|
||||||
|
{
|
||||||
|
$this->vendorDir = $vendorDir;
|
||||||
|
self::initializeIncludeClosure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, list<string>>
|
||||||
|
*/
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string> Array of classname => path
|
||||||
|
*/
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $classMap Class to filename map
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
$paths = (array) $paths;
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
$paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param list<string>|string $paths The PSR-0 base directories
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param list<string>|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||||
|
*
|
||||||
|
* @param string|null $apcuPrefix
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setApcuPrefix($apcuPrefix)
|
||||||
|
{
|
||||||
|
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getApcuPrefix()
|
||||||
|
{
|
||||||
|
return $this->apcuPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
|
||||||
|
if (null === $this->vendorDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prepend) {
|
||||||
|
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||||
|
} else {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
|
||||||
|
if (null !== $this->vendorDir) {
|
||||||
|
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return true|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
$includeFile = self::$includeFile;
|
||||||
|
$includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||||
|
if ($hit) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if (false === $file && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->apcuPrefix) {
|
||||||
|
apcu_add($this->apcuPrefix.$class, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
$this->missingClasses[$class] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||||
|
*
|
||||||
|
* @return array<string, self>
|
||||||
|
*/
|
||||||
|
public static function getRegisteredLoaders()
|
||||||
|
{
|
||||||
|
return self::$registeredLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $class
|
||||||
|
* @param string $ext
|
||||||
|
* @return string|false
|
||||||
|
*/
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
$subPath = $class;
|
||||||
|
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||||
|
$subPath = substr($subPath, 0, $lastPos);
|
||||||
|
$search = $subPath . '\\';
|
||||||
|
if (isset($this->prefixDirsPsr4[$search])) {
|
||||||
|
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||||
|
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||||
|
if (file_exists($file = $dir . $pathEnd)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeIncludeClosure()
|
||||||
|
{
|
||||||
|
if (self::$includeFile !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
self::$includeFile = \Closure::bind(static function($file) {
|
||||||
|
include $file;
|
||||||
|
}, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
396
vendor/composer/InstalledVersions.php
vendored
Normal file
396
vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
use Composer\Semver\VersionParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is copied in every Composer installed project and available to all
|
||||||
|
*
|
||||||
|
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||||
|
*
|
||||||
|
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||||
|
*
|
||||||
|
* @final
|
||||||
|
*/
|
||||||
|
class InstalledVersions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private static $selfDir = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var mixed[]|null
|
||||||
|
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||||
|
*/
|
||||||
|
private static $installed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private static $installedIsLocalDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool|null
|
||||||
|
*/
|
||||||
|
private static $canGetVendors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array[]
|
||||||
|
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static $installedByVendor = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackages()
|
||||||
|
{
|
||||||
|
$packages = array();
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
$packages[] = array_keys($installed['versions']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 === \count($packages)) {
|
||||||
|
return $packages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all package names with a specific type e.g. 'library'
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return list<string>
|
||||||
|
*/
|
||||||
|
public static function getInstalledPackagesByType($type)
|
||||||
|
{
|
||||||
|
$packagesByType = array();
|
||||||
|
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
foreach ($installed['versions'] as $name => $package) {
|
||||||
|
if (isset($package['type']) && $package['type'] === $type) {
|
||||||
|
$packagesByType[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packagesByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package is installed
|
||||||
|
*
|
||||||
|
* This also returns true if the package name is provided or replaced by another package
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @param bool $includeDevRequirements
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (isset($installed['versions'][$packageName])) {
|
||||||
|
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given package satisfies a version constraint
|
||||||
|
*
|
||||||
|
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||||
|
*
|
||||||
|
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||||
|
*
|
||||||
|
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||||
|
* @param string $packageName
|
||||||
|
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||||
|
{
|
||||||
|
$constraint = $parser->parseConstraints((string) $constraint);
|
||||||
|
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||||
|
|
||||||
|
return $provided->matches($constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||||
|
*
|
||||||
|
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||||
|
* whether a given version of a package is installed, and not just whether it exists
|
||||||
|
*
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string Version constraint usable with composer/semver
|
||||||
|
*/
|
||||||
|
public static function getVersionRanges($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranges = array();
|
||||||
|
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||||
|
}
|
||||||
|
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||||
|
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(' || ', $ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||||
|
*/
|
||||||
|
public static function getPrettyVersion($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['pretty_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||||
|
*/
|
||||||
|
public static function getReference($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed['versions'][$packageName]['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $packageName
|
||||||
|
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||||
|
*/
|
||||||
|
public static function getInstallPath($packageName)
|
||||||
|
{
|
||||||
|
foreach (self::getInstalled() as $installed) {
|
||||||
|
if (!isset($installed['versions'][$packageName])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||||
|
*/
|
||||||
|
public static function getRootPackage()
|
||||||
|
{
|
||||||
|
$installed = self::getInstalled();
|
||||||
|
|
||||||
|
return $installed[0]['root'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw installed.php data for custom implementations
|
||||||
|
*
|
||||||
|
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||||
|
*/
|
||||||
|
public static function getRawData()
|
||||||
|
{
|
||||||
|
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
self::$installed = include __DIR__ . '/installed.php';
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
public static function getAllRawData()
|
||||||
|
{
|
||||||
|
return self::getInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you reload the static array from another file
|
||||||
|
*
|
||||||
|
* This is only useful for complex integrations in which a project needs to use
|
||||||
|
* this class but then also needs to execute another project's autoloader in process,
|
||||||
|
* and wants to ensure both projects have access to their version of installed.php.
|
||||||
|
*
|
||||||
|
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||||
|
* the data it needs from this class, then call reload() with
|
||||||
|
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||||
|
* the project in which it runs can then also use this class safely, without
|
||||||
|
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||||
|
*
|
||||||
|
* @param array[] $data A vendor/composer/installed.php data set
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||||
|
*/
|
||||||
|
public static function reload($data)
|
||||||
|
{
|
||||||
|
self::$installed = $data;
|
||||||
|
self::$installedByVendor = array();
|
||||||
|
|
||||||
|
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||||
|
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||||
|
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||||
|
// all installed packages for example
|
||||||
|
self::$installedIsLocalDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getSelfDir()
|
||||||
|
{
|
||||||
|
if (self::$selfDir === null) {
|
||||||
|
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$selfDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array[]
|
||||||
|
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||||
|
*/
|
||||||
|
private static function getInstalled()
|
||||||
|
{
|
||||||
|
if (null === self::$canGetVendors) {
|
||||||
|
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed = array();
|
||||||
|
$copiedLocalDir = false;
|
||||||
|
|
||||||
|
if (self::$canGetVendors) {
|
||||||
|
$selfDir = self::getSelfDir();
|
||||||
|
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||||
|
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||||
|
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||||
|
$installed[] = self::$installedByVendor[$vendorDir];
|
||||||
|
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require $vendorDir.'/composer/installed.php';
|
||||||
|
self::$installedByVendor[$vendorDir] = $required;
|
||||||
|
$installed[] = $required;
|
||||||
|
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
self::$installed = $required;
|
||||||
|
self::$installedIsLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||||
|
$copiedLocalDir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === self::$installed) {
|
||||||
|
// only require the installed.php file if this file is loaded from its dumped location,
|
||||||
|
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||||
|
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||||
|
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||||
|
$required = require __DIR__ . '/installed.php';
|
||||||
|
self::$installed = $required;
|
||||||
|
} else {
|
||||||
|
self::$installed = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||||
|
$installed[] = self::$installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
vendor/composer/LICENSE
vendored
Normal file
21
vendor/composer/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
21
vendor/composer/autoload_classmap.php
vendored
Normal file
21
vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(__DIR__);
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
|
'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
|
||||||
|
'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
|
||||||
|
'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
|
||||||
|
'DateInvalidTimeZoneException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
|
||||||
|
'DateMalformedIntervalStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
|
||||||
|
'DateMalformedPeriodStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
|
||||||
|
'DateMalformedStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
|
||||||
|
'DateObjectError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
|
||||||
|
'DateRangeError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
|
||||||
|
'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php',
|
||||||
|
'SQLite3Exception' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
|
||||||
|
);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user