Slim Framework Guide
Introduction to Slim Framework
Slim is a PHP microframework that helps you quickly write simple yet powerful web applications and APIs. It's built on top of PSR-7 HTTP message interfaces and supports dependency injection, middleware, and fast routing.
Installation and Setup
Installing Slim
# Create new project with Slim skeleton
composer create-project slim/slim-skeleton my-slim-app
# Or install Slim into existing project
composer require slim/slim
composer require slim/psr7
Basic Application Setup
<?php
// public/index.php
require_once __DIR__ . '/../vendor/autoload.php';
use Slim\Factory\AppFactory;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
// Create Slim app
$app = AppFactory::create();
// Add routing middleware
$app->addRoutingMiddleware();
// Add error middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
// Define routes
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write("Hello, Slim Framework!");
return $response;
});
$app->run();
?>
Project Structure
my-slim-app/
├── public/
│ └── index.php
├── src/
│ ├── Application/
│ │ ├── Actions/
│ │ ├── Handlers/
│ │ ├── Middleware/
│ │ └── ResponseEmitter/
│ ├── Domain/
│ │ ├── User/
│ │ └── Product/
│ └── Infrastructure/
│ ├── Persistence/
│ └── Validation/
├── config/
│ ├── container.php
│ ├── middleware.php
│ ├── routes.php
│ └── settings.php
├── templates/
├── logs/
├── tests/
└── composer.json
Routing
Basic Routes
<?php
use Slim\Factory\AppFactory;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
$app = AppFactory::create();
// GET route
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
$name = $args['name'];
$response->getBody()->write("Hello, $name");
return $response;
});
// POST route
$app->post('/users', function (Request $request, Response $response, array $args) {
$data = $request->getParsedBody();
// Create user logic here
$response->getBody()->write(json_encode(['status' => 'created']));
return $response->withHeader('Content-Type', 'application/json');
});
// PUT route
$app->put('/users/{id}', function (Request $request, Response $response, array $args) {
$id = $args['id'];
$data = $request->getParsedBody();
// Update user logic here
$response->getBody()->write(json_encode(['id' => $id, 'status' => 'updated']));
return $response->withHeader('Content-Type', 'application/json');
});
// DELETE route
$app->delete('/users/{id}', function (Request $request, Response $response, array $args) {
$id = $args['id'];
// Delete user logic here
$response->getBody()->write(json_encode(['id' => $id, 'status' => 'deleted']));
return $response->withHeader('Content-Type', 'application/json');
});
?>
Route Groups
<?php
$app->group('/api/v1', function ($group) {
$group->group('/users', function ($group) {
$group->get('', \App\Action\User\ListUsersAction::class);
$group->post('', \App\Action\User\CreateUserAction::class);
$group->get('/{id}', \App\Action\User\ViewUserAction::class);
$group->put('/{id}', \App\Action\User\UpdateUserAction::class);
$group->delete('/{id}', \App\Action\User\DeleteUserAction::class);
});
$group->group('/products', function ($group) {
$group->get('', \App\Action\Product\ListProductsAction::class);
$group->post('', \App\Action\Product\CreateProductAction::class);
$group->get('/{id}', \App\Action\Product\ViewProductAction::class);
});
})->add(\App\Middleware\AuthenticationMiddleware::class);
?>
Route Patterns and Constraints
<?php
// Route with optional parameters
$app->get('/users[/{id}]', function (Request $request, Response $response, array $args) {
$id = $args['id'] ?? null;
if ($id) {
// Show specific user
$response->getBody()->write("User ID: $id");
} else {
// Show all users
$response->getBody()->write("All users");
}
return $response;
});
// Route with constraints
$app->get('/users/{id:[0-9]+}', function (Request $request, Response $response, array $args) {
$id = $args['id'];
$response->getBody()->write("User ID: $id");
return $response;
});
// Route with multiple constraints
$app->get('/archive/{year:[0-9]{4}}/{month:[0-9]{2}}',
function (Request $request, Response $response, array $args) {
$year = $args['year'];
$month = $args['month'];
$response->getBody()->write("Archive for $year-$month");
return $response;
}
);
?>
Controllers and Actions
Action Classes
<?php
// src/Action/User/ListUsersAction.php
namespace App\Action\User;
use App\Domain\User\UserRepository;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
final class ListUsersAction
{
private UserRepository $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function __invoke(Request $request, Response $response): Response
{
$queryParams = $request->getQueryParams();
$page = (int) ($queryParams['page'] ?? 1);
$limit = (int) ($queryParams['limit'] ?? 10);
$users = $this->userRepository->findAll($page, $limit);
$payload = json_encode([
'data' => $users,
'pagination' => [
'page' => $page,
'limit' => $limit,
'total' => $this->userRepository->count()
]
]);
$response->getBody()->write($payload);
return $response->withHeader('Content-Type', 'application/json');
}
}
// src/Action/User/CreateUserAction.php
namespace App\Action\User;
use App\Domain\User\UserRepository;
use App\Domain\User\User;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Valitron\Validator;
final class CreateUserAction
{
private UserRepository $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function __invoke(Request $request, Response $response): Response
{
$data = $request->getParsedBody();
// Validation
$validator = new Validator($data);
$validator->rule('required', ['name', 'email']);
$validator->rule('email', 'email');
$validator->rule('lengthMin', 'name', 2);
if (!$validator->validate()) {
$payload = json_encode([
'error' => 'Validation failed',
'details' => $validator->errors()
]);
$response->getBody()->write($payload);
return $response
->withHeader('Content-Type', 'application/json')
->withStatus(400);
}
// Create user
$user = new User();
$user->setName($data['name']);
$user->setEmail($data['email']);
$userId = $this->userRepository->save($user);
$payload = json_encode([
'id' => $userId,
'message' => 'User created successfully'
]);
$response->getBody()->write($payload);
return $response
->withHeader('Content-Type', 'application/json')
->withStatus(201);
}
}
?>
Controller Base Class
<?php
// src/Controller/BaseController.php
namespace App\Controller;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Psr7\Response as SlimResponse;
abstract class BaseController
{
protected function jsonResponse(
array $data,
int $status = 200,
Response $response = null
): Response {
$response = $response ?: new SlimResponse();
$payload = json_encode($data, JSON_PRETTY_PRINT);
$response->getBody()->write($payload);
return $response
->withHeader('Content-Type', 'application/json')
->withStatus($status);
}
protected function errorResponse(
string $message,
int $status = 400,
array $details = [],
Response $response = null
): Response {
return $this->jsonResponse([
'error' => $message,
'details' => $details
], $status, $response);
}
protected function successResponse(
$data = null,
string $message = 'Success',
Response $response = null
): Response {
$payload = ['message' => $message];
if ($data !== null) {
$payload['data'] = $data;
}
return $this->jsonResponse($payload, 200, $response);
}
}
// Example usage
class UserController extends BaseController
{
public function index(Request $request, Response $response): Response
{
$users = [
['id' => 1, 'name' => 'John Doe'],
['id' => 2, 'name' => 'Jane Smith']
];
return $this->successResponse($users, 'Users retrieved successfully', $response);
}
public function show(Request $request, Response $response, array $args): Response
{
$id = $args['id'];
// Simulate user not found
if ($id > 100) {
return $this->errorResponse('User not found', 404, [], $response);
}
$user = ['id' => $id, 'name' => 'User ' . $id];
return $this->successResponse($user, 'User retrieved successfully', $response);
}
}
?>
Middleware
Creating Custom Middleware
<?php
// src/Middleware/AuthenticationMiddleware.php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Psr7\Response as SlimResponse;
class AuthenticationMiddleware implements MiddlewareInterface
{
public function process(Request $request, RequestHandlerInterface $handler): Response
{
$authHeader = $request->getHeaderLine('Authorization');
if (empty($authHeader)) {
$response = new SlimResponse();
$response->getBody()->write(json_encode([
'error' => 'Authentication required'
]));
return $response
->withHeader('Content-Type', 'application/json')
->withStatus(401);
}
// Extract token from "Bearer TOKEN" format
if (preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
$token = $matches[1];
// Validate token (example validation)
if ($this->validateToken($token)) {
// Add user info to request attributes
$request = $request->withAttribute('user', $this->getUserFromToken($token));
return $handler->handle($request);
}
}
$response = new SlimResponse();
$response->getBody()->write(json_encode([
'error' => 'Invalid authentication token'
]));
return $response
->withHeader('Content-Type', 'application/json')
->withStatus(401);
}
private function validateToken(string $token): bool
{
// Implement your token validation logic
return strlen($token) > 10; // Simple example
}
private function getUserFromToken(string $token): array
{
// Implement user retrieval from token
return ['id' => 1, 'name' => 'John Doe'];
}
}
// src/Middleware/CorsMiddleware.php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CorsMiddleware implements MiddlewareInterface
{
public function process(Request $request, RequestHandlerInterface $handler): Response
{
$response = $handler->handle($request);
return $response
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
}
}
?>
Built-in and Third-party Middleware
<?php
// config/middleware.php
use App\Middleware\AuthenticationMiddleware;
use App\Middleware\CorsMiddleware;
use Slim\Middleware\ContentLengthMiddleware;
return function ($app) {
// CORS middleware (should be early in the stack)
$app->add(CorsMiddleware::class);
// Content length middleware
$app->add(ContentLengthMiddleware::class);
// Authentication middleware for API routes
$app->group('/api', function ($group) {
// Your API routes here
})->add(AuthenticationMiddleware::class);
// Add global error handling middleware
$app->add(function ($request, $handler) {
try {
return $handler->handle($request);
} catch (\Throwable $e) {
$response = new \Slim\Psr7\Response();
$response->getBody()->write(json_encode([
'error' => 'Internal server error',
'message' => $e->getMessage()
]));
return $response
->withHeader('Content-Type', 'application/json')
->withStatus(500);
}
});
};
?>
Dependency Injection
Container Configuration
<?php
// config/container.php
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
return function () {
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
// Database
PDO::class => function (ContainerInterface $c) {
$settings = $c->get('settings')['database'];
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=utf8mb4',
$settings['host'],
$settings['database']
);
return new PDO($dsn, $settings['username'], $settings['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
},
// Repositories
\App\Domain\User\UserRepository::class => function (ContainerInterface $c) {
return new \App\Infrastructure\Persistence\User\DatabaseUserRepository(
$c->get(PDO::class)
);
},
// Services
\App\Domain\User\UserService::class => function (ContainerInterface $c) {
return new \App\Domain\User\UserService(
$c->get(\App\Domain\User\UserRepository::class)
);
},
// Settings
'settings' => [
'database' => [
'host' => $_ENV['DB_HOST'] ?? 'localhost',
'database' => $_ENV['DB_NAME'] ?? 'slim_app',
'username' => $_ENV['DB_USER'] ?? 'root',
'password' => $_ENV['DB_PASS'] ?? '',
],
'app' => [
'name' => 'Slim Application',
'version' => '1.0.0',
]
]
]);
return $containerBuilder->build();
};
?>
Service Classes
<?php
// src/Domain/User/UserService.php
namespace App\Domain\User;
class UserService
{
private UserRepository $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function getAllUsers(int $page = 1, int $limit = 10): array
{
return $this->userRepository->findAll($page, $limit);
}
public function getUserById(int $id): ?User
{
return $this->userRepository->findById($id);
}
public function createUser(array $userData): User
{
$this->validateUserData($userData);
$user = new User();
$user->setName($userData['name']);
$user->setEmail($userData['email']);
$this->userRepository->save($user);
return $user;
}
public function updateUser(int $id, array $userData): User
{
$user = $this->getUserById($id);
if (!$user) {
throw new \RuntimeException('User not found');
}
$this->validateUserData($userData);
if (isset($userData['name'])) {
$user->setName($userData['name']);
}
if (isset($userData['email'])) {
$user->setEmail($userData['email']);
}
$this->userRepository->save($user);
return $user;
}
public function deleteUser(int $id): bool
{
return $this->userRepository->delete($id);
}
private function validateUserData(array $data): void
{
if (empty($data['name'])) {
throw new \InvalidArgumentException('Name is required');
}
if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Valid email is required');
}
}
}
?>
Database Integration
Repository Pattern
<?php
// src/Domain/User/UserRepository.php
namespace App\Domain\User;
interface UserRepository
{
public function findAll(int $page = 1, int $limit = 10): array;
public function findById(int $id): ?User;
public function save(User $user): void;
public function delete(int $id): bool;
public function count(): int;
}
// src/Infrastructure/Persistence/User/DatabaseUserRepository.php
namespace App\Infrastructure\Persistence\User;
use App\Domain\User\User;
use App\Domain\User\UserRepository;
use PDO;
class DatabaseUserRepository implements UserRepository
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function findAll(int $page = 1, int $limit = 10): array
{
$offset = ($page - 1) * $limit;
$stmt = $this->pdo->prepare(
'SELECT * FROM users ORDER BY id DESC LIMIT :limit OFFSET :offset'
);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$users = [];
while ($row = $stmt->fetch()) {
$users[] = $this->hydrateUser($row);
}
return $users;
}
public function findById(int $id): ?User
{
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetch();
return $row ? $this->hydrateUser($row) : null;
}
public function save(User $user): void
{
if ($user->getId()) {
$this->updateUser($user);
} else {
$this->insertUser($user);
}
}
public function delete(int $id): bool
{
$stmt = $this->pdo->prepare('DELETE FROM users WHERE id = :id');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
return $stmt->execute() && $stmt->rowCount() > 0;
}
public function count(): int
{
$stmt = $this->pdo->query('SELECT COUNT(*) FROM users');
return (int) $stmt->fetchColumn();
}
private function insertUser(User $user): void
{
$stmt = $this->pdo->prepare(
'INSERT INTO users (name, email, created_at) VALUES (:name, :email, NOW())'
);
$stmt->bindValue(':name', $user->getName());
$stmt->bindValue(':email', $user->getEmail());
$stmt->execute();
$user->setId((int) $this->pdo->lastInsertId());
}
private function updateUser(User $user): void
{
$stmt = $this->pdo->prepare(
'UPDATE users SET name = :name, email = :email WHERE id = :id'
);
$stmt->bindValue(':id', $user->getId(), PDO::PARAM_INT);
$stmt->bindValue(':name', $user->getName());
$stmt->bindValue(':email', $user->getEmail());
$stmt->execute();
}
private function hydrateUser(array $row): User
{
$user = new User();
$user->setId((int) $row['id']);
$user->setName($row['name']);
$user->setEmail($row['email']);
return $user;
}
}
?>
API Development
RESTful API Example
<?php
// config/routes.php
use App\Action\User\{
ListUsersAction,
ViewUserAction,
CreateUserAction,
UpdateUserAction,
DeleteUserAction
};
return function ($app) {
// API v1 routes
$app->group('/api/v1', function ($group) {
// Users resource
$group->get('/users', ListUsersAction::class);
$group->post('/users', CreateUserAction::class);
$group->get('/users/{id:[0-9]+}', ViewUserAction::class);
$group->put('/users/{id:[0-9]+}', UpdateUserAction::class);
$group->delete('/users/{id:[0-9]+}', DeleteUserAction::class);
// Health check
$group->get('/health', function ($request, $response) {
$payload = json_encode([
'status' => 'healthy',
'timestamp' => date('c'),
'version' => '1.0.0'
]);
$response->getBody()->write($payload);
return $response->withHeader('Content-Type', 'application/json');
});
});
// Handle OPTIONS requests for CORS
$app->options('/{routes:.+}', function ($request, $response) {
return $response;
});
};
?>
API Response Helpers
<?php
// src/Helper/ApiResponse.php
namespace App\Helper;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Psr7\Response as SlimResponse;
class ApiResponse
{
public static function success(
$data = null,
string $message = 'Success',
int $status = 200,
Response $response = null
): Response {
$response = $response ?: new SlimResponse();
$payload = [
'success' => true,
'message' => $message
];
if ($data !== null) {
$payload['data'] = $data;
}
$response->getBody()->write(json_encode($payload, JSON_PRETTY_PRINT));
return $response
->withHeader('Content-Type', 'application/json')
->withStatus($status);
}
public static function error(
string $message,
int $status = 400,
array $details = [],
Response $response = null
): Response {
$response = $response ?: new SlimResponse();
$payload = [
'success' => false,
'error' => $message
];
if (!empty($details)) {
$payload['details'] = $details;
}
$response->getBody()->write(json_encode($payload, JSON_PRETTY_PRINT));
return $response
->withHeader('Content-Type', 'application/json')
->withStatus($status);
}
public static function paginated(
array $data,
int $page,
int $limit,
int $total,
Response $response = null
): Response {
$response = $response ?: new SlimResponse();
$payload = [
'success' => true,
'data' => $data,
'pagination' => [
'page' => $page,
'limit' => $limit,
'total' => $total,
'pages' => ceil($total / $limit)
]
];
$response->getBody()->write(json_encode($payload, JSON_PRETTY_PRINT));
return $response->withHeader('Content-Type', 'application/json');
}
}
?>
Testing
Unit Testing
<?php
// tests/Action/User/ListUsersActionTest.php
namespace Tests\Action\User;
use App\Action\User\ListUsersAction;
use App\Domain\User\UserRepository;
use App\Domain\User\User;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Headers;
use Slim\Psr7\Request as SlimRequest;
use Slim\Psr7\Response;
use Slim\Psr7\Uri;
class ListUsersActionTest extends TestCase
{
use ProphecyTrait;
public function testListUsers()
{
// Mock repository
$userRepository = $this->prophesize(UserRepository::class);
$users = [
$this->createUser(1, 'John Doe', '[email protected]'),
$this->createUser(2, 'Jane Smith', '[email protected]')
];
$userRepository->findAll(1, 10)->willReturn($users);
$userRepository->count()->willReturn(2);
// Create action
$action = new ListUsersAction($userRepository->reveal());
// Create request
$request = $this->createRequest('GET', '/users');
$response = new Response();
// Execute action
$response = $action($request, $response);
// Assertions
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('application/json', $response->getHeaderLine('Content-Type'));
$body = (string) $response->getBody();
$data = json_decode($body, true);
$this->assertArrayHasKey('data', $data);
$this->assertCount(2, $data['data']);
$this->assertArrayHasKey('pagination', $data);
}
private function createRequest(string $method, string $path): Request
{
$uri = new Uri('', '', 80, $path);
$handle = fopen('php://temp', 'w+');
$stream = (new StreamFactory())->createStreamFromResource($handle);
$headers = new Headers();
return new SlimRequest($method, $uri, $headers, [], [], $stream);
}
private function createUser(int $id, string $name, string $email): User
{
$user = new User();
$user->setId($id);
$user->setName($name);
$user->setEmail($email);
return $user;
}
}
?>
Integration Testing
<?php
// tests/Integration/UserApiTest.php
namespace Tests\Integration;
use PHPUnit\Framework\TestCase;
use Slim\App;
use Slim\Factory\AppFactory;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Headers;
use Slim\Psr7\Request;
use Slim\Psr7\Uri;
class UserApiTest extends TestCase
{
private App $app;
protected function setUp(): void
{
$this->app = AppFactory::create();
// Configure your test app here
// Include routes, middleware, etc.
require __DIR__ . '/../../config/routes.php';
require __DIR__ . '/../../config/middleware.php';
}
public function testGetUsers()
{
$request = $this->createRequest('GET', '/api/v1/users');
$response = $this->app->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$body = (string) $response->getBody();
$data = json_decode($body, true);
$this->assertIsArray($data);
$this->assertArrayHasKey('data', $data);
}
public function testCreateUser()
{
$userData = [
'name' => 'Test User',
'email' => '[email protected]'
];
$request = $this->createRequest('POST', '/api/v1/users', $userData);
$response = $this->app->handle($request);
$this->assertEquals(201, $response->getStatusCode());
$body = (string) $response->getBody();
$data = json_decode($body, true);
$this->assertArrayHasKey('id', $data);
$this->assertArrayHasKey('message', $data);
}
private function createRequest(
string $method,
string $path,
array $data = []
): Request {
$uri = new Uri('', '', 80, $path);
$handle = fopen('php://temp', 'w+');
$stream = (new StreamFactory())->createStreamFromResource($handle);
$headers = new Headers();
$headers->addHeader('Content-Type', 'application/json');
if (!empty($data)) {
$stream->write(json_encode($data));
$stream->rewind();
}
return new Request($method, $uri, $headers, [], [], $stream);
}
}
?>
Configuration and Environment
Environment Configuration
<?php
// config/settings.php
return [
'settings' => [
'displayErrorDetails' => $_ENV['APP_DEBUG'] === 'true',
'logError' => true,
'logErrorDetails' => true,
'logger' => [
'name' => 'slim-app',
'path' => __DIR__ . '/../logs/app.log',
'level' => \Monolog\Logger::DEBUG,
],
'database' => [
'host' => $_ENV['DB_HOST'] ?? 'localhost',
'database' => $_ENV['DB_NAME'] ?? 'slim_app',
'username' => $_ENV['DB_USER'] ?? 'root',
'password' => $_ENV['DB_PASS'] ?? '',
],
'jwt' => [
'secret' => $_ENV['JWT_SECRET'] ?? 'your-secret-key',
'algorithm' => 'HS256',
'expires_in' => 3600 // 1 hour
]
]
];
?>
Best Practices
Error Handling
<?php
// src/Middleware/ErrorHandlerMiddleware.php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Exception\HttpNotFoundException;
use Slim\Psr7\Response;
class ErrorHandlerMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
try {
return $handler->handle($request);
} catch (HttpNotFoundException $e) {
return $this->createErrorResponse('Resource not found', 404);
} catch (\InvalidArgumentException $e) {
return $this->createErrorResponse($e->getMessage(), 400);
} catch (\Throwable $e) {
// Log the error
error_log($e->getMessage());
return $this->createErrorResponse('Internal server error', 500);
}
}
private function createErrorResponse(string $message, int $status): ResponseInterface
{
$response = new Response();
$payload = json_encode([
'success' => false,
'error' => $message
]);
$response->getBody()->write($payload);
return $response
->withHeader('Content-Type', 'application/json')
->withStatus($status);
}
}
?>
Performance Optimization
<?php
// Use route caching in production
if (!$_ENV['APP_DEBUG']) {
$app->getRouteCollector()->setCacheFile('/tmp/routes.cache');
}
// Enable OpCache
ini_set('opcache.enable', 1);
ini_set('opcache.memory_consumption', 128);
ini_set('opcache.max_accelerated_files', 4000);
ini_set('opcache.revalidate_freq', 60);
// Database connection pooling
$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
?>
Slim Framework provides a lightweight, fast, and flexible foundation for building modern PHP applications and APIs with clean architecture and PSR compliance.