Slim PHP: Mikro Framework ile Hızlı ve Esnek Web Uygulamaları
Slim PHP: Mikro Framework ile Hızlı ve Esnek Web Uygulamaları
Laravel her projede dogru secim degil. Bazen daha hafif, daha hizli, daha az soyutlama katmani olan bir yaklasim gerekiyor. Slim PHP tam bu noktada devreye giriyor. Sektorel hazir sitelerden API backend'lerine kadar bircok projemde Slim PHP kullandim. Bu yazida Slim 4'un temellerini, projelerimden gercek orneklerle anlatacagim.
Slim PHP Nedir?
Slim PHP, PSR-7 ve PSR-15 standartlarina uygun bir mikro framework. Laravel'in 80+ service provider'i, Eloquent ORM'i, Blade template engine'i varken Slim size sadece su ucu verir: routing, middleware ve dependency injection. Geri kalani siz secersiniz.
Bu minimalizm dezavantaj degil, avantaj. Bir insaat firmasi sitesi icin Laravel kurmak, sinek oldurmeye bazooka ile gitmek gibi. Slim ile ayni siteyi yarim saatte ayaga kaldirabilirsiniz.
Kurulum ve Temel Yapi
composer require slim/slim slim/psr7 slim/twig-view php-di/php-di vlucas/phpdotenv
Proje yapisi:
project/
├── config/
│ ├── dependencies.php # DI container tanimlari
│ └── routes.php # Route tanimlari
├── src/
│ ├── Controllers/
│ ├── Middleware/
│ └── Services/
├── templates/ # Twig template'leri
│ ├── layouts/
│ ├── pages/
│ └── admin/
├── public/
│ └── index.php # Entry point
├── .env
└── composer.json
Entry Point
// public/index.php
<?php
require __DIR__ . '/../vendor/autoload.php';
use DI\ContainerBuilder;
use Slim\Factory\AppFactory;
// .env yukle
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->load();
// DI Container
$containerBuilder = new ContainerBuilder();
$dependencies = require __DIR__ . '/../config/dependencies.php';
$dependencies($containerBuilder);
$container = $containerBuilder->build();
// Slim App
AppFactory::setContainer($container);
$app = AppFactory::create();
// Middleware
$app->addRoutingMiddleware();
$app->addErrorMiddleware(
$_ENV['APP_ENV'] === 'development',
true,
true
);
// Twig middleware
$app->add(\Slim\Views\TwigMiddleware::createFromContainer($app));
// Route'lari yukle
$routes = require __DIR__ . '/../config/routes.php';
$routes($app);
$app->run();
40 satir kod ile calisan bir web uygulamasi. Laravel'in bootstrap sureci 200+ satir ve onlarca service provider yukler. Slim sade ve hizli.
Dependency Injection: PHP-DI
Slim 4, container secimini size birakir. PHP-DI en populer seceneklerden biri:
// config/dependencies.php
use DI\ContainerBuilder;
use Slim\Views\Twig;
use Psr\Container\ContainerInterface;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
// Twig Template Engine
Twig::class => function (ContainerInterface $c) {
$twig = Twig::create(__DIR__ . '/../templates', [
'cache' => $_ENV['APP_ENV'] === 'production'
? __DIR__ . '/../cache/twig'
: false,
'debug' => $_ENV['APP_ENV'] === 'development',
]);
// Global degiskenler
$twig->getEnvironment()->addGlobal('app', [
'name' => $_ENV['APP_NAME'] ?? 'Site Adi',
'url' => $_ENV['APP_URL'] ?? 'http://localhost',
]);
$twig->getEnvironment()->addGlobal('company', [
'name' => $_ENV['COMPANY_NAME'] ?? '',
'phone' => $_ENV['COMPANY_PHONE'] ?? '',
'email' => $_ENV['COMPANY_EMAIL'] ?? '',
'address' => $_ENV['COMPANY_ADDRESS'] ?? '',
]);
return $twig;
},
// PDO Database
PDO::class => function () {
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=utf8mb4',
$_ENV['DB_HOST'] ?? 'localhost',
$_ENV['DB_NAME'] ?? 'app_db'
);
return new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASS'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
},
// Cloud Storage Service
'App\Services\CloudStorageService' => function () {
$provider = $_ENV['STORAGE_PROVIDER'] ?? 'local';
$config = ['provider' => $provider];
if ($provider === 'yandex') {
$config += [
'username' => $_ENV['YANDEX_DISK_USERNAME'],
'password' => $_ENV['YANDEX_DISK_PASSWORD'],
'folder' => $_ENV['YANDEX_DISK_FOLDER'],
];
} elseif ($provider === 's3') {
$config += [
'access_key' => $_ENV['AWS_ACCESS_KEY_ID'],
'secret_key' => $_ENV['AWS_SECRET_ACCESS_KEY'],
'region' => $_ENV['AWS_DEFAULT_REGION'],
'bucket' => $_ENV['AWS_BUCKET'],
];
}
return new App\Services\CloudStorageService($config);
},
]);
};
Laravel'deki config/app.php + AppServiceProvider + auto-discovery yerine tek dosyada tum dependency'ler tanimli. Acik ve okunakli.
Route Tanimlari
// config/routes.php
use Slim\App;
use App\Controllers\HomeController;
use App\Controllers\ProjectController;
use App\Controllers\ContactController;
use App\Controllers\AdminController;
use App\Controllers\BlogController;
return function (App $app) {
// Frontend sayfalari
$app->get('/', [HomeController::class, 'index']);
$app->get('/hizmetler', [ServiceController::class, 'index']);
$app->get('/hizmetler/{slug}', [ServiceController::class, 'detail']);
$app->get('/projeler', [ProjectController::class, 'index']);
$app->get('/projeler/{slug}', [ProjectController::class, 'detail']);
$app->get('/hakkimizda', [HomeController::class, 'about']);
// Iletisim (GET + POST)
$app->get('/iletisim', [ContactController::class, 'index']);
$app->post('/iletisim', [ContactController::class, 'submit']);
// Blog
$app->get('/blog', [BlogController::class, 'index']);
$app->get('/blog/{slug}', [BlogController::class, 'detail']);
// Admin panel (route group)
$app->group('/admin', function ($group) {
$group->get('', [AdminController::class, 'dashboard']);
$group->get('/projeler', [AdminController::class, 'projects']);
$group->post('/projeler/ekle', [AdminController::class, 'addProject']);
$group->delete('/projeler/{id}', [AdminController::class, 'deleteProject']);
$group->post('/upload-media', [AdminController::class, 'uploadMedia']);
$group->get('/media-files', [AdminController::class, 'getMediaFiles']);
});
// API
$app->group('/api', function ($group) {
$group->post('/contact', [ContactController::class, 'apiSubmit']);
$group->get('/projects', [ProjectController::class, 'apiList']);
});
};
Laravel'deki web.php ve api.php'nin birlesmis hali. $app->group() ile route gruplama, middleware atama ayni mantik.
Controller Yapisi
Slim controller'lari saf PHP siniflari. Base class yok, trait yok, magic method yok:
// src/Controllers/HomeController.php
namespace App\Controllers;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Views\Twig;
class HomeController
{
public function __construct(
private Twig $twig
) {}
public function index(Request $request, Response $response): Response
{
return $this->twig->render($response, 'pages/home.twig', [
'title' => 'Ana Sayfa',
'projects' => $this->getRecentProjects(),
'services' => $this->getServices(),
]);
}
public function about(Request $request, Response $response): Response
{
return $this->twig->render($response, 'pages/about.twig', [
'title' => 'Hakkimizda',
]);
}
private function getRecentProjects(): array
{
// DB sorgusu veya statik veri
return [
['title' => 'Villa Projesi', 'slug' => 'villa-projesi', 'image' => '/img/villa.jpg'],
['title' => 'Apartman', 'slug' => 'apartman-projesi', 'image' => '/img/apartman.jpg'],
];
}
}
JSON API Dondurmek
// src/Controllers/ProjectController.php
public function apiList(Request $request, Response $response): Response
{
$projects = $this->getAllProjects();
$response->getBody()->write(json_encode([
'success' => true,
'data' => $projects,
'count' => count($projects),
]));
return $response
->withHeader('Content-Type', 'application/json')
->withStatus(200);
}
Laravel'deki return response()->json() yerine PSR-7 standardi. Daha verbose ama standart uyumlu -- bu response'u herhangi bir PSR-7 uyumlu framework'e tasiyabilirsiniz.
Twig Template Engine
Slim + Twig kombinasyonu, Blade'e cok benzer ama daha strict:
{# templates/layouts/base.twig #}
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>{{ title }} | {{ app.name }}</title>
<meta name="description" content="{{ description ?? app.name }}">
{% block styles %}{% endblock %}
</head>
<body>
{% include 'partials/header.twig' %}
{% block content %}{% endblock %}
{% include 'partials/footer.twig' %}
{% block scripts %}{% endblock %}
</body>
</html>
{# templates/pages/projects.twig #}
{% extends 'layouts/base.twig' %}
{% block content %}
<section class="projects">
<h1>Projelerimiz</h1>
<div class="grid">
{% for project in projects %}
<div class="card">
<img src="{{ project.image }}" alt="{{ project.title }}">
<h3>{{ project.title }}</h3>
<p>{{ project.description|truncate(120) }}</p>
<a href="/projeler/{{ project.slug }}">Detay</a>
</div>
{% else %}
<p>Henuz proje eklenmemis.</p>
{% endfor %}
</div>
</section>
{% endblock %}
Blade vs Twig farklari:
| Blade | Twig |
|---|---|
@extends('layout') |
{% extends 'layout.twig' %} |
@section('content') |
{% block content %} |
@include('partial') |
{% include 'partial.twig' %} |
{{ $variable }} |
{{ variable }} |
@foreach($items as $item) |
{% for item in items %} |
@if($condition) |
{% if condition %} |
Syntax farklari disinda mantik ayni. Twig'in avantaji: sandbox modu ile guvenli template rendering ve auto-escaping varsayilan olarak acik.
Middleware
Slim'de middleware PSR-15 standardi:
// src/Middleware/AuthMiddleware.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\Psr7\Response;
class AuthMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$session = $_SESSION ?? [];
if (empty($session['admin_logged_in'])) {
$response = new Response();
return $response
->withHeader('Location', '/admin/login')
->withStatus(302);
}
return $handler->handle($request);
}
}
Route grubuna middleware eklemek:
$app->group('/admin', function ($group) {
// Admin route'lari...
})->add(new AuthMiddleware());
Multi-Provider Cloud Storage
Projelerimde Slim ile kurdu gum en ilginc yapilardan biri multi-provider storage servisi. Ayni interface ile Yandex Disk, S3, Cloudflare R2 ve lokal depolama arasinda gecis yapabiliyorsunuz:
// src/Services/CloudStorageService.php
namespace App\Services;
class CloudStorageService
{
private string $provider;
private array $config;
public function __construct(array $config)
{
$this->provider = $config['provider'];
$this->config = $config;
}
public function uploadFile(string $filePath, string $key): ?string
{
return match ($this->provider) {
'yandex' => $this->uploadToYandexDisk($filePath, $key),
's3' => $this->uploadToS3($filePath, $key),
'r2' => $this->uploadToR2($filePath, $key),
default => $this->uploadToLocal($filePath, $key),
};
}
private function uploadToYandexDisk(string $filePath, string $key): ?string
{
$webdavUrl = "https://webdav.yandex.com/{$this->config['folder']}/{$key}";
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $webdavUrl,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_POSTFIELDS => file_get_contents($filePath),
CURLOPT_USERPWD => "{$this->config['username']}:{$this->config['password']}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: ' . mime_content_type($filePath),
],
]);
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ($httpCode >= 200 && $httpCode < 300)
? $this->getPublicUrl($key)
: null;
}
private function uploadToS3(string $filePath, string $key): ?string
{
$s3 = new \Aws\S3\S3Client([
'region' => $this->config['region'],
'version' => 'latest',
'credentials' => [
'key' => $this->config['access_key'],
'secret' => $this->config['secret_key'],
],
]);
$result = $s3->putObject([
'Bucket' => $this->config['bucket'],
'Key' => $key,
'SourceFile' => $filePath,
'ACL' => 'public-read',
'CacheControl'=> 'public, max-age=31536000',
]);
return $result['ObjectURL'] ?? null;
}
private function uploadToLocal(string $filePath, string $key): ?string
{
$destination = $this->config['local_path'] . '/' . $key;
$dir = dirname($destination);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
copy($filePath, $destination);
return '/uploads/' . $key;
}
}
.env dosyasinda STORAGE_PROVIDER=yandex yazarsaniz Yandex Disk'e, s3 yazarsaniz AWS'ye, local yazarsaniz sunucudaki diske yukler. Ayni interface, farkli backend.
Slim vs Laravel: Ne Zaman Hangisi?
| Kriter | Slim PHP | Laravel |
|---|---|---|
| Ogrenme egrisi | Dusuk | Orta-Yuksek |
| Kurulum suresi | 5 dakika | 10 dakika |
| Bootstrap suresi | ~5ms | ~30-50ms |
| Bellek kullanimi | ~2-5MB | ~20-40MB |
| ORM | Yok (PDO veya Doctrine) | Eloquent dahil |
| Template | Twig (ayri paket) | Blade dahil |
| Auth sistemi | Kendin yaz | Breeze/Fortify/Jetstream |
| CLI | Yok | Artisan dahil |
| Ekosistem | Kucuk | Devasa |
Slim'i sec eger:
- Sektorel hazir site yapiyorsaniz (insaat, avukat, doktor, restoran)
- Basit API backend gerekiyorsa
- Performans kritikse (her milisaniye onemli)
- Framework'un ne yaptigini tam olarak bilmek istiyorsaniz
- Proje kucuk-orta olcekli ve tek gelistirici
Laravel'i sec eger:
- Karmasik is mantigi varsa (e-ticaret, SaaS, CRM)
- Auth, queue, notification, broadcasting gerekiyorsa
- Buyuk ekiple calisiyorsaniz
- Hizli prototipleme onemli (Eloquent + Blade + Artisan)
- Ekosistem destegi lazimsa (Nova, Horizon, Telescope)
Sektorel Hazir Site Ornegi
Slim PHP ile insaat, avukat, diyetisyen gibi sektorel siteleri hizla uretiyordum. Ortak yapi su sekilde:
Frontend:
/ ................ Ana sayfa
/hizmetler ....... Hizmet listesi
/hizmetler/{slug} Hizmet detay
/projeler ........ Proje galerisi
/projeler/{slug} . Proje detay
/blog ............ Blog listesi
/blog/{slug} ..... Blog detay
/hakkimizda ...... Hakkimizda
/iletisim ........ Iletisim formu
Admin:
/admin ........... Dashboard
/admin/projeler .. Proje yonetimi
/admin/ayarlar ... Site ayarlari
/admin/analytics . Google Analytics
Her sektor icin template degisir ama yapi ayni kalir. Slim'in hafifliginden dolayi ayni sunucuda 10-15 site calistirabilirsiniz. Laravel ile bu sayi 3-4'e duser.
Sonuc
Slim PHP, "framework sana ne verir" degil "sen framework'tan ne istersin" yaklasimiyla calisir. Her sey elinizde, her karar sizin. Bu ozgurluk hem guc hem sorumluluk getirir. ORM yok, kendin sec. Auth yok, kendin yaz. Ama karsiliginda hafif, hizli ve tamamen kontrol edebileceginiz bir uygulama elde edersiniz.
Laravel ile basladim, Slim ile sektorel hazir siteleri hizla urettim, sonra buyuk projeler icin tekrar Laravel'e dondum. Dogru arac, dogru is icin. Slim PHP toolbox'unuzda olmasi gereken bir arac.