Skip to content

Commit 1901206

Browse files
authored
Merge pull request #12 from micilini/phase-13-laravel-integration
phase 13: add laravel integration
2 parents 1697df2 + fde4f2c commit 1901206

11 files changed

Lines changed: 620 additions & 3 deletions

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,52 @@ hello
183183

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

186+
## Laravel integration
187+
188+
PHPSockets can be installed inside Laravel applications through Composer package discovery.
189+
190+
Publish the config:
191+
192+
```bash
193+
php artisan vendor:publish --tag=phpsockets-config
194+
```
195+
196+
Check the package status:
197+
198+
```bash
199+
php artisan phpsockets:status
200+
```
201+
202+
Run SQLite migrations:
203+
204+
```bash
205+
php artisan phpsockets:migrate --driver=sqlite
206+
```
207+
208+
Start the WebSocket chat server from Laravel:
209+
210+
```bash
211+
php artisan phpsockets:serve
212+
```
213+
214+
The package registers:
215+
216+
- `Micilini\PhpSockets\Laravel\PhpSocketsServiceProvider`
217+
- `Micilini\PhpSockets\Laravel\PhpSocketsFacade`
218+
- `phpsockets:serve`
219+
- `phpsockets:migrate`
220+
- `phpsockets:status`
221+
222+
Example usage:
223+
224+
```php
225+
use Micilini\PhpSockets\Laravel\PhpSocketsFacade as PhpSockets;
226+
227+
PhpSockets::bots();
228+
```
229+
230+
Laravel is optional. The native PHP core continues to work standalone.
231+
186232
## Emoji and small attachment support
187233

188234
The chat examples support a composer action button next to the message input.

composer.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@
1919
"require-dev": {
2020
"phpunit/phpunit": "^10.0|^11.0",
2121
"phpstan/phpstan": "^1.10|^2.0",
22-
"friendsofphp/php-cs-fixer": "^3.0"
22+
"friendsofphp/php-cs-fixer": "^3.0",
23+
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
24+
"illuminate/console": "^10.0|^11.0|^12.0|^13.0",
25+
"orchestra/testbench": "^8.0|^9.0|^10.0|^11.0"
2326
},
2427
"suggest": {
2528
"ext-pdo": "Required for SQL storage adapters and migrations.",
2629
"ext-pdo_sqlite": "Required for SQLite storage tests and local persistence.",
27-
"illuminate/support": "Required for Laravel integration."
30+
"illuminate/support": "Required for Laravel service provider, facade and config integration.",
31+
"illuminate/console": "Required for Laravel Artisan commands."
2832
},
2933
"autoload": {
3034
"psr-4": {
@@ -48,5 +52,15 @@
4852
]
4953
},
5054
"minimum-stability": "stable",
51-
"prefer-stable": true
55+
"prefer-stable": true,
56+
"extra": {
57+
"laravel": {
58+
"providers": [
59+
"Micilini\\PhpSockets\\Laravel\\PhpSocketsServiceProvider"
60+
],
61+
"aliases": {
62+
"PhpSockets": "Micilini\\PhpSockets\\Laravel\\PhpSocketsFacade"
63+
}
64+
}
65+
}
5266
}

config/phpsockets.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
return [
6+
/*
7+
|--------------------------------------------------------------------------
8+
| PHPSockets Server
9+
|--------------------------------------------------------------------------
10+
|
11+
| These values are used by the Laravel Artisan commands and by the
12+
| PhpSocketsManager when building a ChatServer instance.
13+
|
14+
*/
15+
16+
'server' => [
17+
'host' => env('PHPSOCKETS_HOST', '127.0.0.1'),
18+
'port' => (int) env('PHPSOCKETS_PORT', 8080),
19+
'max_payload_bytes' => (int) env('PHPSOCKETS_MAX_PAYLOAD_BYTES', 4 * 1024 * 1024),
20+
'tick_microseconds' => (int) env('PHPSOCKETS_TICK_MICROSECONDS', 10000),
21+
'connection_limit' => (int) env('PHPSOCKETS_CONNECTION_LIMIT', 100),
22+
'debug' => (bool) env('PHPSOCKETS_DEBUG', false),
23+
],
24+
25+
/*
26+
|--------------------------------------------------------------------------
27+
| PHPSockets Chat
28+
|--------------------------------------------------------------------------
29+
*/
30+
31+
'chat' => [
32+
'max_display_name_length' => (int) env('PHPSOCKETS_MAX_DISPLAY_NAME_LENGTH', 40),
33+
'max_room_name_length' => (int) env('PHPSOCKETS_MAX_ROOM_NAME_LENGTH', 80),
34+
'max_private_group_members' => (int) env('PHPSOCKETS_MAX_PRIVATE_GROUP_MEMBERS', 20),
35+
'allow_guest_sessions' => (bool) env('PHPSOCKETS_ALLOW_GUEST_SESSIONS', true),
36+
'history_limit' => (int) env('PHPSOCKETS_HISTORY_LIMIT', 50),
37+
'max_attachment_bytes' => (int) env('PHPSOCKETS_MAX_ATTACHMENT_BYTES', 2 * 1024 * 1024),
38+
'max_attachment_file_name_length' => (int) env('PHPSOCKETS_MAX_ATTACHMENT_FILE_NAME_LENGTH', 180),
39+
40+
'allowed_attachment_mime_types' => [
41+
'image/png',
42+
'image/jpeg',
43+
'image/gif',
44+
'application/pdf',
45+
'text/plain',
46+
],
47+
],
48+
49+
/*
50+
|--------------------------------------------------------------------------
51+
| PHPSockets Storage
52+
|--------------------------------------------------------------------------
53+
|
54+
| memory:
55+
| Default runtime storage.
56+
|
57+
| sqlite/mysql/pgsql:
58+
| Used by migrations and future persistent Laravel examples.
59+
|
60+
*/
61+
62+
'storage' => [
63+
'driver' => env('PHPSOCKETS_STORAGE', 'memory'),
64+
65+
'database' => env('PHPSOCKETS_DATABASE', database_path('phpsockets.sqlite')),
66+
67+
'dsn' => env('PHPSOCKETS_DSN'),
68+
69+
'username' => env('PHPSOCKETS_DB_USERNAME'),
70+
71+
'password' => env('PHPSOCKETS_DB_PASSWORD'),
72+
],
73+
];
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Micilini\PhpSockets\Laravel\Commands;
6+
7+
use Illuminate\Console\Command;
8+
use Micilini\PhpSockets\Database\MigrationRunner;
9+
use Micilini\PhpSockets\Laravel\PhpSocketsManager;
10+
use Throwable;
11+
12+
final class MigrateCommand extends Command
13+
{
14+
protected $signature = 'phpsockets:migrate
15+
{--driver= : Storage driver: sqlite, mysql or pgsql}
16+
{--database= : SQLite database path override}
17+
{--force : Run without confirmation in production}';
18+
19+
protected $description = 'Run PHPSockets database migrations.';
20+
21+
public function handle(PhpSocketsManager $manager): int
22+
{
23+
$driver = $this->option('driver');
24+
$driver = is_string($driver) && $driver !== ''
25+
? strtolower(trim($driver))
26+
: $manager->storageDriver();
27+
28+
if ($driver === 'memory') {
29+
$this->components->warn('The memory storage driver does not need migrations.');
30+
31+
return self::SUCCESS;
32+
}
33+
34+
if (!in_array($driver, ['sqlite', 'mysql', 'pgsql'], true)) {
35+
$this->components->error("Unsupported migration driver: {$driver}");
36+
37+
return self::FAILURE;
38+
}
39+
40+
if ($this->laravel->environment('production') && !$this->option('force')) {
41+
if (!$this->confirm('You are running PHPSockets migrations in production. Continue?')) {
42+
return self::FAILURE;
43+
}
44+
}
45+
46+
$database = $this->option('database');
47+
48+
try {
49+
$pdo = $manager->pdo(
50+
driver: $driver,
51+
databaseOverride: is_string($database) && $database !== '' ? $database : null,
52+
);
53+
54+
(new MigrationRunner($pdo))->run($driver);
55+
56+
$this->components->info("PHPSockets {$driver} migrations completed.");
57+
58+
return self::SUCCESS;
59+
} catch (Throwable $exception) {
60+
$this->components->error($exception->getMessage());
61+
62+
return self::FAILURE;
63+
}
64+
}
65+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Micilini\PhpSockets\Laravel\Commands;
6+
7+
use Illuminate\Console\Command;
8+
use Micilini\PhpSockets\Laravel\PhpSocketsManager;
9+
use Throwable;
10+
11+
final class ServeCommand extends Command
12+
{
13+
protected $signature = 'phpsockets:serve
14+
{--host= : Override configured WebSocket host}
15+
{--port= : Override configured WebSocket port}
16+
{--debug : Enable debug logs for this run}';
17+
18+
protected $description = 'Start the PHPSockets WebSocket chat server from Laravel.';
19+
20+
public function handle(PhpSocketsManager $manager): int
21+
{
22+
$overrides = [];
23+
$host = $this->option('host');
24+
25+
if (is_string($host) && $host !== '') {
26+
$overrides['host'] = $host;
27+
}
28+
29+
$port = $this->option('port');
30+
31+
if (is_string($port) && $port !== '') {
32+
$overrides['port'] = (int) $port;
33+
}
34+
35+
if ((bool) $this->option('debug')) {
36+
$overrides['debug'] = true;
37+
}
38+
39+
try {
40+
$serverConfig = $manager->serverConfig($overrides);
41+
$server = $manager->server($overrides);
42+
43+
$this->components->info("Starting PHPSockets on {$serverConfig->host}:{$serverConfig->port}");
44+
$this->line('Press CTRL+C to stop.');
45+
46+
$server->run();
47+
48+
return self::SUCCESS;
49+
} catch (Throwable $exception) {
50+
$this->components->error($exception->getMessage());
51+
52+
return self::FAILURE;
53+
}
54+
}
55+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Micilini\PhpSockets\Laravel\Commands;
6+
7+
use Illuminate\Console\Command;
8+
use Micilini\PhpSockets\Laravel\PhpSocketsManager;
9+
10+
final class StatusCommand extends Command
11+
{
12+
protected $signature = 'phpsockets:status';
13+
14+
protected $description = 'Show PHPSockets Laravel configuration status.';
15+
16+
public function handle(PhpSocketsManager $manager): int
17+
{
18+
$serverConfig = $manager->serverConfig();
19+
$chatConfig = $manager->chatConfig();
20+
21+
$this->components->info('PHPSockets is installed.');
22+
23+
$this->table(['Option', 'Value'], [
24+
['Host', $serverConfig->host],
25+
['Port', (string) $serverConfig->port],
26+
['Max payload bytes', (string) $serverConfig->maxPayloadBytes],
27+
['Connection limit', (string) $serverConfig->connectionLimit],
28+
['Debug logs', $serverConfig->enableDebugLogs ? 'yes' : 'no'],
29+
['History limit', (string) $chatConfig->historyLimit],
30+
['Max attachment bytes', (string) $chatConfig->maxAttachmentBytes],
31+
['Storage driver', $manager->storageDriver()],
32+
]);
33+
34+
return self::SUCCESS;
35+
}
36+
}

src/Laravel/PhpSocketsFacade.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Micilini\PhpSockets\Laravel;
6+
7+
use Illuminate\Support\Facades\Facade;
8+
9+
/**
10+
* @method static \Micilini\PhpSockets\Chat\Bot\BotManager bots()
11+
* @method static \Micilini\PhpSockets\Chat\ChatKernel kernel()
12+
* @method static void run()
13+
* @method static void stop()
14+
* @method static \Micilini\PhpSockets\Server\WebSocketServer webSocketServer()
15+
*
16+
* @see \Micilini\PhpSockets\Chat\ChatServer
17+
*/
18+
final class PhpSocketsFacade extends Facade
19+
{
20+
protected static function getFacadeAccessor(): string
21+
{
22+
return 'phpsockets';
23+
}
24+
}

0 commit comments

Comments
 (0)