Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,52 @@ hello

Bots are intentionally simple in v1. They do not call external AI APIs or run asynchronous jobs.

## Laravel integration

PHPSockets can be installed inside Laravel applications through Composer package discovery.

Publish the config:

```bash
php artisan vendor:publish --tag=phpsockets-config
```

Check the package status:

```bash
php artisan phpsockets:status
```

Run SQLite migrations:

```bash
php artisan phpsockets:migrate --driver=sqlite
```

Start the WebSocket chat server from Laravel:

```bash
php artisan phpsockets:serve
```

The package registers:

- `Micilini\PhpSockets\Laravel\PhpSocketsServiceProvider`
- `Micilini\PhpSockets\Laravel\PhpSocketsFacade`
- `phpsockets:serve`
- `phpsockets:migrate`
- `phpsockets:status`

Example usage:

```php
use Micilini\PhpSockets\Laravel\PhpSocketsFacade as PhpSockets;

PhpSockets::bots();
```

Laravel is optional. The native PHP core continues to work standalone.

## Emoji and small attachment support

The chat examples support a composer action button next to the message input.
Expand Down
20 changes: 17 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@
"require-dev": {
"phpunit/phpunit": "^10.0|^11.0",
"phpstan/phpstan": "^1.10|^2.0",
"friendsofphp/php-cs-fixer": "^3.0"
"friendsofphp/php-cs-fixer": "^3.0",
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
"illuminate/console": "^10.0|^11.0|^12.0|^13.0",
"orchestra/testbench": "^8.0|^9.0|^10.0|^11.0"
},
"suggest": {
"ext-pdo": "Required for SQL storage adapters and migrations.",
"ext-pdo_sqlite": "Required for SQLite storage tests and local persistence.",
"illuminate/support": "Required for Laravel integration."
"illuminate/support": "Required for Laravel service provider, facade and config integration.",
"illuminate/console": "Required for Laravel Artisan commands."
},
"autoload": {
"psr-4": {
Expand All @@ -48,5 +52,15 @@
]
},
"minimum-stability": "stable",
"prefer-stable": true
"prefer-stable": true,
"extra": {
"laravel": {
"providers": [
"Micilini\\PhpSockets\\Laravel\\PhpSocketsServiceProvider"
],
"aliases": {
"PhpSockets": "Micilini\\PhpSockets\\Laravel\\PhpSocketsFacade"
}
}
}
}
73 changes: 73 additions & 0 deletions config/phpsockets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

return [
/*
|--------------------------------------------------------------------------
| PHPSockets Server
|--------------------------------------------------------------------------
|
| These values are used by the Laravel Artisan commands and by the
| PhpSocketsManager when building a ChatServer instance.
|
*/

'server' => [
'host' => env('PHPSOCKETS_HOST', '127.0.0.1'),
'port' => (int) env('PHPSOCKETS_PORT', 8080),
'max_payload_bytes' => (int) env('PHPSOCKETS_MAX_PAYLOAD_BYTES', 4 * 1024 * 1024),
'tick_microseconds' => (int) env('PHPSOCKETS_TICK_MICROSECONDS', 10000),
'connection_limit' => (int) env('PHPSOCKETS_CONNECTION_LIMIT', 100),
'debug' => (bool) env('PHPSOCKETS_DEBUG', false),
],

/*
|--------------------------------------------------------------------------
| PHPSockets Chat
|--------------------------------------------------------------------------
*/

'chat' => [
'max_display_name_length' => (int) env('PHPSOCKETS_MAX_DISPLAY_NAME_LENGTH', 40),
'max_room_name_length' => (int) env('PHPSOCKETS_MAX_ROOM_NAME_LENGTH', 80),
'max_private_group_members' => (int) env('PHPSOCKETS_MAX_PRIVATE_GROUP_MEMBERS', 20),
'allow_guest_sessions' => (bool) env('PHPSOCKETS_ALLOW_GUEST_SESSIONS', true),
'history_limit' => (int) env('PHPSOCKETS_HISTORY_LIMIT', 50),
'max_attachment_bytes' => (int) env('PHPSOCKETS_MAX_ATTACHMENT_BYTES', 2 * 1024 * 1024),
'max_attachment_file_name_length' => (int) env('PHPSOCKETS_MAX_ATTACHMENT_FILE_NAME_LENGTH', 180),

'allowed_attachment_mime_types' => [
'image/png',
'image/jpeg',
'image/gif',
'application/pdf',
'text/plain',
],
],

/*
|--------------------------------------------------------------------------
| PHPSockets Storage
|--------------------------------------------------------------------------
|
| memory:
| Default runtime storage.
|
| sqlite/mysql/pgsql:
| Used by migrations and future persistent Laravel examples.
|
*/

'storage' => [
'driver' => env('PHPSOCKETS_STORAGE', 'memory'),

'database' => env('PHPSOCKETS_DATABASE', database_path('phpsockets.sqlite')),

'dsn' => env('PHPSOCKETS_DSN'),

'username' => env('PHPSOCKETS_DB_USERNAME'),

'password' => env('PHPSOCKETS_DB_PASSWORD'),
],
];
65 changes: 65 additions & 0 deletions src/Laravel/Commands/MigrateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Micilini\PhpSockets\Laravel\Commands;

use Illuminate\Console\Command;
use Micilini\PhpSockets\Database\MigrationRunner;
use Micilini\PhpSockets\Laravel\PhpSocketsManager;
use Throwable;

final class MigrateCommand extends Command
{
protected $signature = 'phpsockets:migrate
{--driver= : Storage driver: sqlite, mysql or pgsql}
{--database= : SQLite database path override}
{--force : Run without confirmation in production}';

protected $description = 'Run PHPSockets database migrations.';

public function handle(PhpSocketsManager $manager): int
{
$driver = $this->option('driver');
$driver = is_string($driver) && $driver !== ''
? strtolower(trim($driver))
: $manager->storageDriver();

if ($driver === 'memory') {
$this->components->warn('The memory storage driver does not need migrations.');

return self::SUCCESS;
}

if (!in_array($driver, ['sqlite', 'mysql', 'pgsql'], true)) {
$this->components->error("Unsupported migration driver: {$driver}");

return self::FAILURE;
}

if ($this->laravel->environment('production') && !$this->option('force')) {
if (!$this->confirm('You are running PHPSockets migrations in production. Continue?')) {
return self::FAILURE;
}
}

$database = $this->option('database');

try {
$pdo = $manager->pdo(
driver: $driver,
databaseOverride: is_string($database) && $database !== '' ? $database : null,
);

(new MigrationRunner($pdo))->run($driver);

$this->components->info("PHPSockets {$driver} migrations completed.");

return self::SUCCESS;
} catch (Throwable $exception) {
$this->components->error($exception->getMessage());

return self::FAILURE;
}
}
}
55 changes: 55 additions & 0 deletions src/Laravel/Commands/ServeCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Micilini\PhpSockets\Laravel\Commands;

use Illuminate\Console\Command;
use Micilini\PhpSockets\Laravel\PhpSocketsManager;
use Throwable;

final class ServeCommand extends Command
{
protected $signature = 'phpsockets:serve
{--host= : Override configured WebSocket host}
{--port= : Override configured WebSocket port}
{--debug : Enable debug logs for this run}';

protected $description = 'Start the PHPSockets WebSocket chat server from Laravel.';

public function handle(PhpSocketsManager $manager): int
{
$overrides = [];
$host = $this->option('host');

if (is_string($host) && $host !== '') {
$overrides['host'] = $host;
}

$port = $this->option('port');

if (is_string($port) && $port !== '') {
$overrides['port'] = (int) $port;
}

if ((bool) $this->option('debug')) {
$overrides['debug'] = true;
}

try {
$serverConfig = $manager->serverConfig($overrides);
$server = $manager->server($overrides);

$this->components->info("Starting PHPSockets on {$serverConfig->host}:{$serverConfig->port}");
$this->line('Press CTRL+C to stop.');

$server->run();

return self::SUCCESS;
} catch (Throwable $exception) {
$this->components->error($exception->getMessage());

return self::FAILURE;
}
}
}
36 changes: 36 additions & 0 deletions src/Laravel/Commands/StatusCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Micilini\PhpSockets\Laravel\Commands;

use Illuminate\Console\Command;
use Micilini\PhpSockets\Laravel\PhpSocketsManager;

final class StatusCommand extends Command
{
protected $signature = 'phpsockets:status';

protected $description = 'Show PHPSockets Laravel configuration status.';

public function handle(PhpSocketsManager $manager): int
{
$serverConfig = $manager->serverConfig();
$chatConfig = $manager->chatConfig();

$this->components->info('PHPSockets is installed.');

$this->table(['Option', 'Value'], [
['Host', $serverConfig->host],
['Port', (string) $serverConfig->port],
['Max payload bytes', (string) $serverConfig->maxPayloadBytes],
['Connection limit', (string) $serverConfig->connectionLimit],
['Debug logs', $serverConfig->enableDebugLogs ? 'yes' : 'no'],
['History limit', (string) $chatConfig->historyLimit],
['Max attachment bytes', (string) $chatConfig->maxAttachmentBytes],
['Storage driver', $manager->storageDriver()],
]);

return self::SUCCESS;
}
}
24 changes: 24 additions & 0 deletions src/Laravel/PhpSocketsFacade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Micilini\PhpSockets\Laravel;

use Illuminate\Support\Facades\Facade;

/**
* @method static \Micilini\PhpSockets\Chat\Bot\BotManager bots()
* @method static \Micilini\PhpSockets\Chat\ChatKernel kernel()
* @method static void run()
* @method static void stop()
* @method static \Micilini\PhpSockets\Server\WebSocketServer webSocketServer()
*
* @see \Micilini\PhpSockets\Chat\ChatServer
*/
final class PhpSocketsFacade extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'phpsockets';
}
}
Loading
Loading