PHP

PHP ile REST API Oluşturma

PHP ile REST API Oluşturma: Kendi API'nizi Adım Adım Nasıl Geliştirirsiniz?

Selamlar herkese! Bugün sizlerle oldukça heyecan verici ve bir o kadar da pratik bir konuyu, yani PHP ile REST API oluşturma sürecini konuşmak istiyorum. Eğer bir geliştiriciyseniz veya web uygulamalarıyla ilgileniyorsanız, API’ların ne kadar merkezi bir rol oynadığını bilirsiniz. Uygulamalarımızın birbiriyle konuşmasını sağlayan bu köprüler, modern yazılım mimarilerinin olmazsa olmazı. Hadi gelin, kendi API’mizi sıfırdan nasıl inşa edeceğimizi adım adım inceleyelim.

 

Neden PHP ile REST API Oluşturma?

“Neden PHP?” diye düşünebilirsiniz. PHP, sunucu tarafı programlama dillerinin en popülerlerinden biri. Geniş bir topluluğa sahip olması, esnekliği, hızlı geliştirme süreçleri ve birçok hosting paketinde varsayılan olarak bulunması sayesinde, PHP ile REST API oluşturma süreci hem erişilebilir hem de oldukça verimli olabilir. Küçük veya orta ölçekli projelerden, büyük kurumsal uygulamalara kadar birçok senaryoda PHP, API geliştirmek için güçlü bir seçenektir.

Peki, nedir bu REST API? REST (Representational State Transfer), web servisleri tasarlamak için bir mimari stildir. Temelde, bir sunucudaki kaynaklara (örneğin, kullanıcılar, ürünler, makaleler) HTTP metotları (GET, POST, PUT, DELETE) aracılığıyla erişmek ve onları manipüle etmek üzerine kuruludur. En popüler veri formatı ise genellikle JSON’dır.

 

Adım 1: Temelleri Atmak – Proje Yapısı ve .htaccess

PHP ile REST API oluşturma yolculuğumuza projemizin temelini atarak başlıyoruz. Temiz bir klasör yapısı, projemizin düzenli ve yönetilebilir kalmasını sağlar.

/api_projem/
├── .htaccess
├── index.php
├── config/
│   └── database.php
├── models/
│   └── User.php
├── controllers/
│   └── UserController.php
├── utils/
│   └── Response.php

 

.htaccess Dosyası: Temiz URL’ler ve Yönlendirme

 

API’mızın güzel ve anlamlı URL’lere sahip olması için .htaccess dosyasını kullanacağız. Bu dosya, tüm istekleri index.php‘ye yönlendirecek ve daha sonra bu dosya, isteğin hangi kaynağa ait olduğunu çözümleyecektir.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Bu kod, mevcut bir dosya veya dizin değilse, tüm istekleri index.php‘ye yönlendirir. QSA (Query String Append), sorgu parametrelerini de korurken, L (Last), kural işlemeyi durdurur.

 

Adım 2: Veritabanı Bağlantısı – config/database.php

API’mızın bir veri kaynağına ihtiyacı olacak. Genellikle bu bir veritabanıdır. MySQL veya PostgreSQL gibi ilişkisel veritabanları yaygın olarak kullanılır. PDO (PHP Data Objects) kullanarak güvenli ve esnek bir veritabanı bağlantısı kuracağız.

<?php
class Database {
    private $host = "localhost";
    private $db_name = "api_db";
    private $username = "root";
    private $password = "";
    public $conn;

    public function getConnection() {
        $this->conn = null;
        try {
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
            $this->conn->exec("set names utf8");
        } catch(PDOException $exception) {
            echo "Veritabanı bağlantı hatası: " . $exception->getMessage();
        }
        return $this->conn;
    }
}
?>

Bu sınıf, veritabanı bağlantısını yönetir ve bir PDO nesnesi döndürür. Hata durumunda mesajı yakalar ve gösterir. Gerçek bir projede bu bilgileri bir ortam değişkeni veya ayrı bir yapılandırma dosyasından okumak daha güvenli olacaktır.

 

Adım 3: Model Katmanı – Kaynakları Tanımlama (models/User.php)

Model, veritabanındaki bir tabloyu veya bir veri kaynağını temsil eder. Örneğin, bir User (Kullanıcı) modeli, kullanıcılarla ilgili veritabanı işlemlerini (oluşturma, okuma, güncelleme, silme) yönetecek.

<?php
class User {
    private $conn;
    private $table_name = "users";

    public $id;
    public $name;
    public $email;
    public $created_at;

    public function __construct($db) {
        $this->conn = $db;
    }

    // Tüm kullanıcıları oku
    public function read() {
        $query = "SELECT id, name, email, created_at FROM " . $this->table_name . " ORDER BY created_at DESC";
        $stmt = $this->conn->prepare($query);
        $stmt->execute();
        return $stmt;
    }

    // Yeni kullanıcı oluştur
    public function create() {
        $query = "INSERT INTO " . $this->table_name . " SET name=:name, email=:email";
        $stmt = $this->conn->prepare($query);

        // Güvenlik için verileri temizle
        $this->name = htmlspecialchars(strip_tags($this->name));
        $this->email = htmlspecialchars(strip_tags($this->email));

        // Parametreleri bağla
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":email", $this->email);

        if ($stmt->execute()) {
            return true;
        }
        return false;
    }

    // Tek bir kullanıcıyı oku (ID ile)
    public function readOne() {
        $query = "SELECT id, name, email, created_at FROM " . $this->table_name . " WHERE id = ? LIMIT 0,1";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(1, $this->id);
        $stmt->execute();
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($row) {
            $this->name = $row['name'];
            $this->email = $row['email'];
            $this->created_at = $row['created_at'];
            return true;
        }
        return false;
    }

    // Kullanıcıyı güncelle
    public function update() {
        $query = "UPDATE " . $this->table_name . " SET name = :name, email = :email WHERE id = :id";
        $stmt = $this->conn->prepare($query);

        $this->name = htmlspecialchars(strip_tags($this->name));
        $this->email = htmlspecialchars(strip_tags($this->email));
        $this->id = htmlspecialchars(strip_tags($this->id));

        $stmt->bindParam(':name', $this->name);
        $stmt->bindParam(':email', $this->email);
        $stmt->bindParam(':id', $this->id);

        if ($stmt->execute()) {
            return true;
        }
        return false;
    }

    // Kullanıcıyı sil
    public function delete() {
        $query = "DELETE FROM " . $this->table_name . " WHERE id = ?";
        $stmt = $this->conn->prepare($query);

        $this->id = htmlspecialchars(strip_tags($this->id));
        $stmt->bindParam(1, $this->id);

        if ($stmt->execute()) {
            return true;
        }
        return false;
    }
}
?>

Bu User sınıfı, temel CRUD (Create, Read, Update, Delete) operasyonlarını içeriyor. Gördüğünüz gibi, SQL enjeksiyonlarına karşı htmlspecialchars ve strip_tags ile basit bir temizlik de yapıyorum.

 

Adım 4: Kontrolcü Katmanı – İstekleri İşleme (controllers/UserController.php)

Kontrolcüler (Controllers), gelen HTTP isteklerini yönetir, model ile etkileşime girer ve yanıtı hazırlar. Her kaynak için ayrı bir kontrolcü veya tek bir kontrolcü içinde farklı metotlar tanımlayabilirsiniz. Biz burada User kaynağı için UserController.php‘yi örnek alacağız.

Bu bölümde, genellikle her HTTP metoduna (GET, POST, PUT, DELETE) karşılık gelen fonksiyonlar bulunur.

<?php
// users/read.php (Örnek bir read operasyonu)
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");

include_once '../config/database.php';
include_once '../models/User.php';

$database = new Database();
$db = $database->getConnection();

$user = new User($db);

$stmt = $user->read();
$num = $stmt->rowCount();

if ($num > 0) {
    $users_arr = array();
    $users_arr["records"] = array();

    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        extract($row);
        $user_item = array(
            "id" => $id,
            "name" => $name,
            "email" => $email,
            "created_at" => $created_at
        );
        array_push($users_arr["records"], $user_item);
    }
    http_response_code(200);
    echo json_encode($users_arr);
} else {
    http_response_code(404);
    echo json_encode(array("message" => "Kullanıcı bulunamadı."));
}

// users/create.php (Örnek bir create operasyonu)
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once '../config/database.php';
include_once '../models/User.php';

$database = new Database();
$db = $database->getConnection();

$user = new User($db);

$data = json_decode(file_get_contents("php://input"));

if (
    !empty($data->name) &&
    !empty($data->email)
) {
    $user->name = $data->name;
    $user->email = $data->email;

    if ($user->create()) {
        http_response_code(201);
        echo json_encode(array("message" => "Kullanıcı başarıyla oluşturuldu."));
    } else {
        http_response_code(503); // Service Unavailable
        echo json_encode(array("message" => "Kullanıcı oluşturulamadı."));
    }
} else {
    http_response_code(400); // Bad Request
    echo json_encode(array("message" => "Veri eksik. Kullanıcı oluşturulamadı."));
}

// Diğer CRUD operasyonları (readOne, update, delete) için de benzer yapılar kurulur.
?>

Yukarıdaki kodlar, her bir API uç noktası için ayrı PHP dosyaları şeklinde olabilir. index.php dosyamızda bu dosyaları $_SERVER['REQUEST_METHOD'] ve $_SERVER['REQUEST_URI'] kullanarak dinamik olarak çağıracağız. Access-Control-Allow-Origin: * gibi başlıklar, API’nızın farklı alan adlarından erişilebilir olmasını sağlar (CORS politikası).

 

Adım 5: Yönlendirme ve Ana Uygulama (index.php)

index.php dosyamız, gelen isteği dinleyecek, URL’yi ve HTTP metodunu analiz edecek ve uygun kontrolcü metodunu çağıracaktır. Bu kısım, PHP ile REST API oluşturma sürecinin orkestra şefi gibidir.

<?php
// Gerekli dosyaları dahil et
include_once 'config/database.php';
include_once 'models/User.php';

// Veritabanı bağlantısı
$database = new Database();
$db = $database->getConnection();

// User modelini başlat
$user = new User($db);

// Gelen isteği ve URL'yi al
$request_method = $_SERVER['REQUEST_METHOD'];
$request_uri = explode('/', trim($_SERVER['REQUEST_URI'], '/'));

// API yolunu belirle (örn: /users/1)
// İlk eleman genellikle 'api' veya 'v1' gibi bir ön ek olabilir
// Bizim örneğimizde doğrudan 'users' ile başlayacak
$resource = isset($request_uri[0]) ? $request_uri[0] : '';
$id = isset($request_uri[1]) ? (int)$request_uri[1] : null;

// JSON çıktısı için header ayarla
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *"); // Tüm domainlerden erişime izin ver

switch ($resource) {
    case 'users':
        if ($request_method == 'GET') {
            if ($id) {
                // Tek kullanıcı oku
                $user->id = $id;
                if ($user->readOne()) {
                    http_response_code(200);
                    echo json_encode(array(
                        "id" => $user->id,
                        "name" => $user->name,
                        "email" => $user->email,
                        "created_at" => $user->created_at
                    ));
                } else {
                    http_response_code(404);
                    echo json_encode(array("message" => "Kullanıcı bulunamadı."));
                }
            } else {
                // Tüm kullanıcıları oku
                $stmt = $user->read();
                $num = $stmt->rowCount();

                if ($num > 0) {
                    $users_arr = array();
                    $users_arr["records"] = array();
                    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                        extract($row);
                        $user_item = array(
                            "id" => $id,
                            "name" => $name,
                            "email" => $email,
                            "created_at" => $created_at
                        );
                        array_push($users_arr["records"], $user_item);
                    }
                    http_response_code(200);
                    echo json_encode($users_arr);
                } else {
                    http_response_code(404);
                    echo json_encode(array("message" => "Kullanıcı bulunamadı."));
                }
            }
        } elseif ($request_method == 'POST') {
            // Kullanıcı oluştur
            header("Access-Control-Allow-Methods: POST");
            header("Access-Control-Max-Age: 3600");
            header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

            $data = json_decode(file_get_contents("php://input"));

            if (
                !empty($data->name) &&
                !empty($data->email)
            ) {
                $user->name = $data->name;
                $user->email = $data->email;

                if ($user->create()) {
                    http_response_code(201);
                    echo json_encode(array("message" => "Kullanıcı başarıyla oluşturuldu."));
                } else {
                    http_response_code(503);
                    echo json_encode(array("message" => "Kullanıcı oluşturulamadı."));
                }
            } else {
                http_response_code(400);
                echo json_encode(array("message" => "Veri eksik. Kullanıcı oluşturulamadı."));
            }
        } elseif ($request_method == 'PUT') {
            // Kullanıcı güncelle
            header("Access-Control-Allow-Methods: PUT");
            header("Access-Control-Max-Age: 3600");
            header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

            $data = json_decode(file_get_contents("php://input"));

            if (
                !empty($id) &&
                !empty($data->name) &&
                !empty($data->email)
            ) {
                $user->id = $id;
                $user->name = $data->name;
                $user->email = $data->email;

                if ($user->update()) {
                    http_response_code(200);
                    echo json_encode(array("message" => "Kullanıcı başarıyla güncellendi."));
                } else {
                    http_response_code(503);
                    echo json_encode(array("message" => "Kullanıcı güncellenemedi."));
                }
            } else {
                http_response_code(400);
                echo json_encode(array("message" => "Veri eksik veya kullanıcı ID'si belirtilmedi."));
            }
        } elseif ($request_method == 'DELETE') {
            // Kullanıcı sil
            header("Access-Control-Allow-Methods: DELETE");
            header("Access-Control-Max-Age: 3600");
            header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

            if (!empty($id)) {
                $user->id = $id;

                if ($user->delete()) {
                    http_response_code(200);
                    echo json_encode(array("message" => "Kullanıcı başarıyla silindi."));
                } else {
                    http_response_code(503);
                    echo json_encode(array("message" => "Kullanıcı silinemedi."));
                }
            } else {
                http_response_code(400);
                echo json_encode(array("message" => "Kullanıcı ID'si belirtilmedi."));
            }
        } else {
            http_response_code(405); // Method Not Allowed
            echo json_encode(array("message" => "Desteklenmeyen HTTP metodu."));
        }
        break;
    default:
        http_response_code(404); // Not Found
        echo json_encode(array("message" => "Kaynak bulunamadı."));
        break;
}
?>

Bu index.php dosyası, gelen isteği parse ederek hangi kaynağın (örneğin ‘users’) ve hangi işlemin (GET, POST vb.) istendiğini belirler. Ardından, ilgili model metodunu çağırır ve uygun HTTP durum koduyla (200 OK, 201 Created, 404 Not Found, 500 Internal Server Error gibi) JSON formatında bir yanıt döner.

 

 

Adım 6: Hata Yönetimi ve Yanıt Biçimlendirme (utils/Response.php – İsteğe Bağlı)

API’nızın tutarlı yanıtlar vermesi için basit bir yanıt sınıfı oluşturmak iyi bir uygulamadır. Bu, başarılı ve hatalı yanıtları standart bir formatta döndürmenize yardımcı olur.

<?php
class Response {
    public static function json($data, $status_code = 200) {
        header("Content-Type: application/json; charset=UTF-8");
        http_response_code($status_code);
        echo json_encode($data);
        exit(); // Yanıtı gönderdikten sonra scripti sonlandır
    }

    public static function error($message, $status_code = 400) {
        self::json(array("message" => $message), $status_code);
    }
}
?>

Artık kontrolcülerimizde Response::json($data, 200); veya Response::error("Veri eksik.", 400); gibi çağrılarla daha temiz kod yazabiliriz.

 

İşte bu kadar! PHP ile REST API oluşturma sürecinin temel adımlarını tamamlamış olduk. Artık kullanıcı verilerini listeleyebilen, yeni kullanıcı oluşturabilen, güncelleyebilen ve silebilen temel bir REST API‘niz var.

Elbette, bu başlangıç seviyesi bir rehberdi. Gerçek dünya uygulamaları için ek olarak şunları düşünmeniz gerekir:

  • Güvenlik: API anahtarları, JWT (JSON Web Tokens) ile kimlik doğrulama, OAuth 2.0 gibi daha gelişmiş kimlik doğrulama mekanizmaları.
  • Doğrulama (Validation): Gelen verilerin doğruluğunu ve formatını kontrol etmek.
  • Hata Yakalama ve Loglama: Detaylı hata yönetimi ve hataları kaydetme.
  • Rate Limiting: API’nıza yapılan isteklerin sayısını sınırlamak.
  • Önbellekleme: Performansı artırmak için API yanıtlarını önbelleğe almak.
  • Dokümantasyon: API’nızı kullanan diğer geliştiriciler için (Swagger/OpenAPI gibi araçlarla) dokümantasyon oluşturmak.
  • Unit Testleri: API uç noktalarınızı otomatize edilmiş testlerle sınamak.
  • Çerçeveler (Frameworks): Symfony, Laravel, Lumen gibi PHP çerçeveleri, bu süreçleri çok daha hızlı ve kolay hale getiren hazır araçlar sunar. Küçük bir API için yukarıdaki yaklaşım uygun olsa da, büyük projelerde bir çerçeve kullanmak genellikle en iyi yoldur.

Umarım bu rehber, kendi PHP ile REST API oluşturma serüveninize başlamanız için size ilham vermiştir. Unutmayın, pratik yapmak ve sürekli öğrenmek bu alanda gelişmenin anahtarıdır. Haydi kodlamaya!

Emre NACAR

Merhaba! Ben Emre Nacar, full stack web developer olarak yaklaşık 10 yıldır kurumsal web siteleri, e-ticaret siteleri ve firmalara özel web tabanlı uygulamalar geliştiriyorum.2013 yılından itibaren yurtiçi veya yurtdışı olarak 200 den fazla projede web tabanlı yazılım geliştirici olarak yer almış, kodlama, veritabanı geliştirme, arayüz tasarımı gibi bir çok alanda görev almış bulunmaktayım. KKTC ve İstanbul gibi farklı şehirlerde Junior Developer, Senior Developer, Proje Yöneticisi olarak çalıştıktan sonra son olarak Gaziantep’de Yazılım Uzmanı olarak çalışmalarımı sürdürmekteyim.

İlgili Makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu