Composer ile kendi paketimi Packagist'te yayımlamak
Bir PHP paketini sıfırdan yapılandırıp Packagist'e göndererek başkalarının kullanımına açtım; adımları aktarıyorum.
Birkaç projede aynı yardımcı sınıfı kopyalayıp yapıştırdığımı fark ettim. Her seferinde güncel mi eski mi diye karşılaştırıyordum. Bir gün “bunu Composer paketi yapsam?” diye düşündüm ve denedim. Sonuç tahmin ettiğimden daha kolay oldu.
Bu yazıda sıfırdan bir PHP paketi yapılandırmayı ve Packagist (PHP paket deposu) üzerinde yayımlamayı adım adım anlatacağım. Packagist, Composer’ın varsayılan olarak baktığı genel paketi deposu.
Paket yapısı
Bir Composer paketi aslında belirli bir yapıya sahip klasörden ibaret. Temel düzende şunlar olmalı:
benim-paketim/
├── src/
│ └── YardimciSinif.php
├── tests/
│ └── YardimciSinifTest.php
├── composer.json
├── .gitignore
└── README.md
src/ altında PHP dosyalarınız, tests/ altında testler, ve her şeyin merkezi olan composer.json.
composer.json dosyasını hazırlamak
{
"name": "muhammetsafak/yardimci",
"description": "Günlük PHP geliştirmede işime yarayan yardımcı sınıflar",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Muhammet Şafak",
"email": "ornek@ornek.com"
}
],
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"Muhammetsafak\\Yardimci\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Muhammetsafak\\Yardimci\\Tests\\": "tests/"
}
}
}
Birkaç önemli nokta:
name:sahip/paketbiçiminde. Packagist’te eşsiz olmalı.autoload: PSR-4 kuralıyla namespace ile klasör eşleştirmesi yapılıyor.Muhammetsafak\Yardimci\namespace’indeki sınıflarsrc/altında aranıyor.require-dev: Yalnızca geliştirme sırasında gereken bağımlılıklar buraya giriyor — tüketicinincomposer installyapmasında indirilmiyor.
type: "library" alanını doğru yazmak önemli. Packagist bu alana bakarak paketi nasıl sınıflandıracağına karar veriyor. Bir Laravel paketi yapıyorsanız type: "library" yine geçerli; Laravel’e özgü service provider’lar extra alanında tanımlanıyor.
Basit bir sınıf yazmak
<?php
namespace Muhammetsafak\Yardimci;
class Metin
{
public static function kisalt(string $metin, int $uzunluk = 100, string $son = '...'): string
{
if (mb_strlen($metin) <= $uzunluk) {
return $metin;
}
return mb_substr($metin, 0, $uzunluk) . $son;
}
public static function slug(string $metin): string
{
$metin = mb_strtolower($metin, 'UTF-8');
$metin = str_replace(
['ç', 'ş', 'ğ', 'ı', 'İ', 'ö', 'ü'],
['c', 's', 'g', 'i', 'i', 'o', 'u'],
$metin
);
$metin = preg_replace('/[^a-z0-9\s-]/', '', $metin);
$metin = preg_replace('/[\s-]+/', '-', $metin);
return trim($metin, '-');
}
}
Bu sınıfı yazmadan önce fark ettiğim bir şey: kütüphanede kullanılan tüm fonksiyonlar mb_ önekli olmalı. strlen yerine mb_strlen, strtolower yerine mb_strtolower. Kütüphane başkası tarafından kullanılacaksa ve o kişinin metin verisi UTF-8 dışında bir encoding içeriyorsa, düz string fonksiyonları sessizce yanlış sonuç üretebilir.
GitHub’a göndermek
Packagist GitHub deposuyla çalışıyor. Paketi bir GitHub deposuna göndermek gerekiyor.
Sürüm etiketleri (tag) önemli. Composer etiketlere bakarak hangi sürümü indireceğine karar veriyor. Semantic versioning kuralına göre:
git tag v1.0.0
git push origin v1.0.0
v1.0.0, v1.0.1, v1.1.0 gibi etiketler Composer tarafından sürüm olarak algılanıyor.
Etiket olmadan da dev-main gibi dal ismiyle paket kullanılabilir; ama bu kararlı bir bağımlılık değildir. Tüketicinin composer.json’unda "minimum-stability": "dev" ayarlanması gerekiyor ve bu pratikte önerilmiyor. Düzgün etiketlenmiş sürümler kullanmak, tüketicilerin tam olarak hangi kodu aldığını bilmesini sağlıyor.
Packagist’e kaydetmek
packagist.org’a GitHub hesabınızla giriş yapın. “Submit” (Gönder) sayfasında GitHub deponuzun URL’ini yapıştırın — Packagist composer.json’u okuyup paketi oluşturuyor.
Bundan sonra:
composer require muhammetsafak/yardimci
komutuyla herkes paketi projelerine ekleyebiliyor.
Otomatik güncelleme için webhook
Paketi her güncellediğinizde Packagist’i haberdar etmek için webhook kurmak gerekiyor. GitHub deposunun ayarlarından “Webhooks” bölümüne girin; Packagist size bir URL ve token veriyor. Bu ikisini webhook olarak ekleyin. Bundan sonra her git push’ta Packagist otomatik güncellenecek.
Webhook kurmadan da Packagist panelinden manuel “Force Update” tetikleyebilirsiniz — ama bu tek seferlik bir çözüm. Webhook, yeni sürüm etiketlediğinizde birkaç dakika içinde Packagist’in güncel bilgiyi görmesini sağlıyor.
.gitignore ve dağıtım dosyaları
Pakete .gitignore eklemenin yanı sıra bir .gitattributes dosyası oluşturmak iyi alışkanlık. Bu dosyada test klasörü ve geliştirme araçlarını dışarıda bırakabilirsiniz:
/tests export-ignore
/.github export-ignore
phpunit.xml export-ignore
Böylece tüketici composer install --prefer-dist yaptığında yalnızca src/ içeriği indiriliyor; testler ve CI yapılandırmaları indirilmiyor. Paketi kullanan projenin vendor/ klasörü gereksiz yere şişmiyor.
İlk yayım deneyimi
Paketi Packagist’e gönderip başka bir projede composer require ile kurunca garip bir his oluştu. Kendi yazdığım kodu bağımlılık olarak kullanmak. Sonraki güncellemede tüm projelerde composer update yeterli.
Bir hatayı düzelttim, v1.0.1 etiketi bastım, GitHub’a gönderdim. Webhook sayesinde Packagist hemen güncellendi. Bağımlı projelerde composer update muhammetsafak/yardimci ile son sürüm geldi. Bu döngüyü ilk yaşadığımda kopyalayıp yapıştırmanın ne kadar kırılgan bir yöntem olduğunu bir kez daha anladım.
Kütüphane yazmak büyük bir iş gibi görünüyor ama küçük, tek amacı olan paketler gerçekten faydalı. Kopyalayıp yapıştırmak yerine versiyonlanan, paylaşılabilir kod. Bu alışkanlık zaman içinde kodu daha modüler düşünmeye de yönlendiriyor.