Cost Savings
When you hire dedicated PHP developers through Support Resort, you get codebase continuity with a depth of experience you will not find elsewhere. 72% of our clients have stayed for 5+ years because their developers have stayed and consistently delivered valuable outcomes. Reach maximum development velocity with experienced, secure-coding-trained PHP developers in India who will be with you for the long haul.
Risk-Free Trial
One week on real projects.
No payment unless you continue.
Truly Dedicated
One developer,
full-time, works only for you.
Month-to-Month
No lock-in. Cancel anytime.
Most stay for years.
Average developer experience of 10.6 years
72% of clients have stayed 5+ years
22 years in business - clients retained for up to 19 years
All developers have complete secure coding training
Pre-tested on internal projects before deployment
Average developer experience of 10.6 years
72% of clients have stayed 5+ years
22 years in business - clients retained for up to 19 years
All developers have complete secure coding training
Pre-tested on internal projects before deployment
Average developer experience of 10.6 years
72% of clients have stayed 5+ years
22 years in business - clients retained for up to 19 years
All developers have complete secure coding training
Pre-tested on internal projects before deployment
Core PHP Stack
Modern Development
Specializations
<?php
// PHP 8.3 - Production WebSocket with Security Best Practices
declare(strict_types=1);
namespace App\WebSocket;
use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\Frame;
use App\Domain\ValueObjects\{UserId, RoomId};
use App\Application\Services\{AuthService, MessageService};
use App\Application\DTOs\WebSocketEventDTO;
use App\Infrastructure\{MonologLogger, PrometheusMetrics, RateLimiter};
use App\Shared\{Sanitizer, AppConfig};
final readonly class WebSocketServer
{
private Server $server;
private ConnectionManager $connections;
private MessageService $messages;
private AuthService $auth;
private MonologLogger $logger;
private PrometheusMetrics $metrics;
private RateLimiter $rateLimiter;
public function __construct(private AppConfig $config)
{
$this->initServices();
$this->createServer();
}
private function initServices(): void
{
$this->logger = new MonologLogger($this->config->get('logging.channel'));
$this->metrics = new PrometheusMetrics($this->config->get('metrics.namespace'));
$redis = new \Redis();
try {
$redis->connect($this->config->get('redis.host'), $this->config->get('redis.port'));
} catch (\RedisException $e) {
$this->logger->error('Redis connection failed', ['error' => $e->getMessage()]);
throw $e;
}
$this->rateLimiter = new RateLimiter($redis, 100, 60);
$this->messages = new MessageService($redis, $this->logger);
$this->auth = new AuthService($this->config->get('auth.jwtSecret'), $redis);
$this->connections = new ConnectionManager($redis, $this->logger);
$this->logger->info('Services initialized');
}
private function createServer(): void
{
$this->server = new Server(
$this->config->get('websocket.host'),
$this->config->get('websocket.port')
);
$this->server->set([
'worker_num' => $this->config->get('websocket.workers', 4),
'max_connection' => 10000,
'heartbeat_check_interval' => 30,
'heartbeat_idle_time' => 60,
'enable_coroutine' => true,
'package_max_length' => 65536, // 64KB max message
]);
$this->server->on('Start', $this->onStart(...));
$this->server->on('Open', $this->onOpen(...));
$this->server->on('Message', $this->onMessage(...));
$this->server->on('Close', $this->onClose(...));
$this->server->on('Request', $this->onHttpRequest(...));
}
// Sanitize input to prevent XSS
private function sanitize(string $input, int $maxLen = 1000): string
{
$clean = strip_tags($input);
$clean = htmlspecialchars($clean, ENT_QUOTES | ENT_HTML5, 'UTF-8');
return mb_substr(trim($clean), 0, $maxLen);
}
// Validate room ID format
private function validateRoomId(string $roomId): ?RoomId
{
if (strlen($roomId) > 64 || !preg_match('/^[a-zA-Z0-9_-]+$/', $roomId)) {
return null;
}
return new RoomId(rawurlencode($roomId));
}
// Add security headers to response
private function addSecurityHeaders(Response $response): void
{
$response->header('X-Content-Type-Options', 'nosniff');
$response->header('X-Frame-Options', 'DENY');
$response->header('X-XSS-Protection', '1; mode=block');
$response->header('Content-Security-Policy', "default-src 'self'");
$response->header('Referrer-Policy', 'strict-origin-when-cross-origin');
}
public function onStart(Server $server): void
{
$this->logger->info('Server started', [
'host' => $this->config->get('websocket.host'),
'port' => $this->config->get('websocket.port')
]);
$this->metrics->registerGauge('websocket_connections', 0);
}
public function onOpen(Server $server, Request $request): void
{
try {
// Auth check
$token = $request->header['authorization'] ?? '';
$user = $this->auth->validateToken($token);
if (!$user) {
$server->disconnect($request->fd, 1008, 'Unauthorized');
return;
}
// Rate limit
if (!$this->rateLimiter->check($user->getId()->toString())) {
$server->disconnect($request->fd, 1008, 'Rate limit exceeded');
return;
}
// Validate room ID
parse_str($request->server['query_string'] ?? '', $query);
$roomId = $this->validateRoomId($query['room'] ?? '');
if (!$roomId) {
$server->disconnect($request->fd, 1008, 'Invalid room ID');
return;
}
// Check room access
if (!$this->auth->canAccessRoom($user->getId(), $roomId)) {
$server->disconnect($request->fd, 1008, 'Access denied');
return;
}
// Register connection
$this->connections->add($request->fd, $user->getId(), $roomId);
// Send acknowledgment
$server->push($request->fd, json_encode([
'type' => 'connected',
'payload' => [
'userId' => $user->getId()->toString(),
'roomId' => $roomId->toString()
]
], JSON_THROW_ON_ERROR));
$this->metrics->incrementGauge('websocket_connections');
$this->logger->info('Connected', ['fd' => $request->fd, 'user' => $user->getId()->toString()]);
} catch (\Throwable $e) {
$this->logger->error('Connection error', ['error' => $e->getMessage()]);
$server->disconnect($request->fd, 1011, 'Server error');
}
}
public function onMessage(Server $server, Frame $frame): void
{
$timer = $this->metrics->startTimer('message_duration');
try {
$conn = $this->connections->get($frame->fd);
if (!$conn) {
throw new \RuntimeException('Connection not found');
}
// Size check
if (strlen($frame->data) > 65536) {
$this->sendError($server, $frame->fd, 'MESSAGE_TOO_LARGE');
return;
}
$data = json_decode($frame->data, true, 512, JSON_THROW_ON_ERROR);
$event = WebSocketEventDTO::fromArray($data);
match ($event->type) {
'message' => $this->handleChatMessage($server, $frame->fd, $event, $conn),
'typing' => $this->handleTyping($server, $conn, (bool)($event->payload['is_typing'] ?? false)),
'heartbeat' => $server->push($frame->fd, json_encode(['type' => 'pong'], JSON_THROW_ON_ERROR)),
default => $this->logger->warning('Unknown event', ['type' => $event->type])
};
} catch (\JsonException) {
$this->sendError($server, $frame->fd, 'INVALID_JSON');
} catch (\Throwable $e) {
$this->logger->error('Message error', ['error' => $e->getMessage()]);
$this->sendError($server, $frame->fd, 'SERVER_ERROR');
} finally {
$timer();
}
}
private function handleChatMessage(Server $server, int $fd, WebSocketEventDTO $event, object $conn): void
{
$text = $this->sanitize($event->payload['text'] ?? '');
if (empty($text)) return;
$message = $this->messages->create($text, $conn->userId, $conn->roomId);
$this->broadcastToRoom($conn->roomId, [
'type' => 'message',
'payload' => $message->toArray()
]);
}
private function handleTyping(Server $server, object $conn, bool $isTyping): void
{
$this->broadcastToRoom($conn->roomId, [
'type' => 'typing',
'payload' => ['userId' => $conn->userId->toString(), 'isTyping' => $isTyping]
], $conn->fd);
}
private function sendError(Server $server, int $fd, string $code): void
{
if ($this->server->isEstablished($fd)) {
$server->push($fd, json_encode(['type' => 'error', 'code' => $code], JSON_THROW_ON_ERROR));
}
}
private function broadcastToRoom(RoomId $roomId, array $data, ?int $exclude = null): void
{
$json = json_encode($data, JSON_THROW_ON_ERROR);
foreach ($this->connections->getByRoom($roomId) as $fd => $conn) {
if ($fd !== $exclude && $this->server->isEstablished($fd)) {
$this->server->push($fd, $json);
}
}
}
public function onClose(Server $server, int $fd): void
{
$conn = $this->connections->remove($fd);
if ($conn) {
$this->broadcastToRoom($conn->roomId, [
'type' => 'user_left',
'payload' => ['userId' => $conn->userId->toString()]
]);
$this->metrics->decrementGauge('websocket_connections');
$this->logger->info('Disconnected', ['fd' => $fd]);
}
}
public function onHttpRequest(Request $request, Response $response): void
{
$this->addSecurityHeaders($response);
match ($request->server['path_info']) {
'/health' => $this->handleHealth($response),
'/metrics' => $this->handleMetrics($response),
default => $response->status(404)->end('Not Found')
};
}
private function handleHealth(Response $response): void
{
$response->header('Content-Type', 'application/json');
$response->end(json_encode([
'status' => 'healthy',
'connections' => $this->connections->count(),
'timestamp' => time()
], JSON_THROW_ON_ERROR));
}
private function handleMetrics(Response $response): void
{
$response->header('Content-Type', 'text/plain');
$response->end($this->metrics->export());
}
public function start(): void
{
$this->server->start();
}
}
// Bootstrap
$server = new WebSocketServer(new AppConfig(__DIR__ . '/../config'));
$server->start();Developer Continuity
When you hire PHP developers in India from us, they stay for years. They learn your systems and become increasingly valuable over time.
Battle-Tested Before Deployment
We don't assign developers to production systems before testing them on our own projects. Your systems aren't their training ground.
Secure Coding Training
All developers complete secure coding training. They write code that protects your data, your users and your reputation.
Incredible Experience
Our developers have an average of 10.6 years of experience. Some of our Lead developers have decades of experience. You won't find a more experienced team elsewhere.
Senior Manager Attention
Every inquiry is handled personally by senior managers. Your first point of contact will be someone who can actually help.
$100 AI Credits Included
$100/month in AI credits included. Your developer can use AI tools (with your consent) to accelerate development.
Build New Systems
Ship new systems with code you can trust
Modernize Existing Systems
Make legacy systems reliable and maintainable
We're not a platform matching you with strangers. We employ developers who build careers here. See how that changes everything.
Cost Savings
Pre-Deployment Testing
Track Record
Client Retention
Staff Longevity
Secure Coding Training
" I have to say that in my entire life I have never ever come across the dedication to detail and the willingness to work at high pressure levels to deadlines as I have experienced with your employees. Your company has my respect, I never thought things would work out as well as they have. Congratulations to you all for such a wonderful service. "
Graeme
" I am amazed with Bidhun. He is very responsive to tasks that I give him. His communication is excellent - way above my expectations and the quality of his work is superior to anyone I have worked with before. He is to be commended on his attendance and commitment to my projects. "
AK
" I just wanted to let you know that I am very pleased with your service. The programmer assigned to me is doing a fine job. He seems to work consistently, he communicates clearly, and he offers good insights concerning our projects. I appreciate his short accurate daily project reports. "
Paul
" Under no circumstances can I lose my developer. I'd rather lose my right arm than him. "
CF
" Thank you so much for all your detailed responses. I have never dealt with a programming company that is so professional. "
Brian
" I find your company and service to be VERY professional and I get more and more excited about our future work! "
Eric
Get In Touch
Reach out - a senior manager will personally discuss your PHP project needs.
Get Matched
We pair you with a developer we've already vetted and tested on internal projects.
Try Risk-Free
One week of real work on your projects. No payment unless you want to continue.
Build Together
Month-to-month from there. Your developer learns your systems and stays.
Skilled PHP Developer
Solid foundation
One-week obligation-free trial
No credit card required
Seasoned PHP Developer
Most popular
One-week obligation-free trial
No credit card required
Lead PHP Developer
Complex systems
One-week obligation-free trial
No credit card required
Tell us what skills you are looking for and we will send you a discount code.
Get no-fuss access to seasoned staff by the week. No minimum commitment. Just extra capacity when you need it.
Software Testers
Full-stack QA: manual, automation, performance, security, mobile.
US$499/week
Web Designers
UI/UX, Figma, responsive design, brand consistency.
US$499/week
Server Administrators
Linux, Docker, security, installations, upgrades, etc.
US$499/week
All from the same trusted partner. 22 years in business. Staff who stay.
Enterprise professionalism. Boutique attention.
Contact Us