Özel Rector geri uyarlama kuralı yazma
Bir bakışta
“Bir bakışta” başlıklı bölümNextPDF backport ardışık düzeni, nextpdf/nextpdf projesinin PHP 8.4 kaynağını PHP 8.1’de ve çekirdek için PHP 7.4’te çalışabilecek şekilde geri uyarlar. Dil özelliklerinin çoğu için Rector’ün yerleşik geri uyarlama setlerini, Rector’ün kapsamadığı özellikler içinse küçük bir özel kural kümesini kullanır.
Bu kılavuz, yeni bir özel kuralın nasıl ekleneceğini gösterir. Depoda zaten bulunan üç kuralla aynı yapıyı izler: DowngradeAsymmetricVisibilityRector, DowngradeCloneWithRector ve DowngradeTraitConstantsRector. Üçü de rector/rules/ içinde bulunur, rector/config/ içinde kayıtlıdır ve tests/Rector/ içinde fikstür tabanlı testlere sahiptir.
Bu kılavuzu, bir NextPDF özelliği derleme hedefinin desteklemediği bir PHP söz dizimi kullandığında ve Rector’ün bunun için yerleşik bir geri uyarlaması bulunmadığında kullanın. Başlamadan önce, Rector’de bunun için gerçekten bir kural bulunmadığını doğrulayın. Yerleşik withDowngradeSets() zinciri, salt okunur sınıfları, tür bildirimli sınıf sabitlerini, boru operatörünü ve diğer pek çok özelliği zaten ele alır.
Kurulum
“Kurulum” başlıklı bölümÖzel kuralları nextpdf-backport deposu içinde yazıp çalıştırırsınız. Geliştirme araçları bu deponun composer.json dosyasında bildirilir.
- Backport deposunu klonlayın ve bağımlılıklarını yükleyin.
- Rector ile PHPUnit’in çözümlendiğini doğrulayın.
git clone https://github.com/nextpdf-labs/backport.gitcd backportcomposer installvendor/bin/rector --versionvendor/bin/phpunit --versionDerleme çıktısı PHP 8.1 veya 7.4 hedeflese de, kurallar 8.4 söz dizimi ağaçlarını işlediği için deponun çalışması için PHP 8.4 gerekir. composer.jsonrequire, php sürümünü >=8.4 <9.0 olarak sabitler. Özel kurallar NextPDF\Backport\ ad alanı altında otomatik yüklenir ve bu ad alanı rector/rules/ dizinine eşlenir; testler ise NextPDF\Backport\Tests\ altında otomatik yüklenir ve tests/ dizinine eşlenir.
Kuralın anatomisi
“Kuralın anatomisi” başlıklı bölümHer özel kural Rector\Rector\AbstractRector sınıfını genişletir ve üç yöntem uygular. Sözleşme, mevcut üç kuralın tamamı için aynıdır.
| Üye | Tür | Amaç |
|---|---|---|
getRuleDefinition() | RuleDefinition | Kural belgesi oluşturucu için, insan tarafından okunabilir bir açıklama ve before/after CodeSample çiftleri. |
getNodeTypes() | array<class-string<Node>> | Kuralın ziyaret ettiği soyut söz dizimi ağacı (AST) düğüm sınıfları. Rector, refactor() yöntemini yalnızca bu sınıflar için çağırır. |
refactor(Node $node) | ?Node veya `Stmt[] | null` |
Düğüm türü bildirimi, kuralın nerede çalışacağını belirler. DowngradeAsymmetricVisibilityRector, [Property::class, Param::class] hedefler; çünkü asimetrik görünürlük (public private(set)) hem bir sınıf özelliğinde hem de yükseltilmiş bir kurucu parametresinde görünebilir. DowngradeTraitConstantsRector, tüm trait gövdesini tek geçişte yeniden yazdığı için [Trait_::class] hedefler. DowngradeCloneWithRector, [FileNode::class, Return_::class, Expression::class] hedefler; çünkü clone-with (clone($obj, [...])) hem return hem de atama konumlarında görünebilir; dosya başına bir sayacı sıfırlamak için FileNode ziyaretini kullanır.
Bir kuralın null değerini refactor() yönteminden döndürmesi “değişiklik yok” anlamına gelir. Düğüm döndürmesi bir değiştirmeyi belirtir. Stmt[] deyim listesi döndürmesi ise bir deyimi birkaç deyime genişletir. DowngradeCloneWithRector, tek bir return clone($this, [...]); ifadesini bir clone atamasına, geçersiz kılma başına bir özellik atamasına ve son bir return ifadesine bu şekilde dönüştürür.
Yerleşik setlerin halihazırda yaptıkları
“Yerleşik setlerin halihazırda yaptıkları” başlıklı bölümİki ardışık düzen yapılandırması ->withDowngradeSets(php81: true) ve ->withDowngradeSets(php74: true) çağrılarını kullanır. Bu setler, hedef için geçerli tüm yerleşik geri uyarlama kurallarını zincirler. Özel kurallar yalnızca boşluklar için vardır: PHP 8.4 asimetrik görünürlük, PHP 8.5 clone-with ve PHP 8.2 trait sabitleri. Rector bunların hiçbirini kendi başına geri uyarlamaz. Özel kuralı yalnızca aynı boşluğu doğruladıktan sonra yazın.
Adım adım: bir kural yazma
“Adım adım: bir kural yazma” başlıklı bölümBu süreç yeni bir kural ekler. Çalışan örnek, mevcut kuralları temel alır; kendi özelliğinizi bunun yerine koyun.
- Kural sınıfını
rector/rules/içinde oluşturun. AdınıDowngrade<Feature>Rectorolarak belirleyin ve mevcut otomatik yükleme eşlemesinin onu seçebilmesi içinNextPDF\Backportad alanına yerleştirin. - Kural sınıfını
Rector\Rector\AbstractRectorile genişletin ve sınıfıfinalolarak işaretleyin. - Kuralın ihtiyaç duyduğu en dar AST düğüm sınıfı kümesini döndürmek için
getNodeTypes()yöntemini uygulayın. Daha dar bir küme, Rector’ün daha az düğüm ziyaret etmesini sağlar. - Kuralın
refactor()yöntemini uygulayın. Düğümün bildirilen türlerden biri olduğunu doğrulayın, yalnızca hedeflediğiniz tam söz dizimiyle eşleşen durumları dönüştürün ve yeni düğümü,Stmt[]değerini veya değişiklik olmadığındanulldöndürün. - Kuralın
getRuleDefinition()yöntemini, ele aldığı her ayrı durum için bir before/afterCodeSampleiçerecek şekilde uygulayın. - Dosyayı PHPStan düzey 10’da tutun: her parametre, dönüş ve özellik için tür belirtin ve dizi şekillerini tanımlamak için PHPDoc jeneriklerini kullanın.
Asimetrik görünürlük kuralı, eksiksiz en küçük örnektir. Set görünürlüğü bayraklarını kaldırır ve temel bir okuma görünürlüğünün kaldığından emin olur:
<?php
declare(strict_types=1);
namespace NextPDF\Backport;
use PhpParser\Modifiers;use PhpParser\Node;use PhpParser\Node\Param;use PhpParser\Node\Stmt\Property;use Rector\Rector\AbstractRector;use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class DowngradeAsymmetricVisibilityRector extends AbstractRector{ public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( 'Remove asymmetric visibility modifiers (public private(set) -> public)', [ new CodeSample( 'public private(set) float $x = 0.0;', 'public float $x = 0.0;', ), ], ); }
/** * @return array<class-string<Node>> */ public function getNodeTypes(): array { return [Property::class, Param::class]; }
public function refactor(Node $node): ?Node { \assert($node instanceof Property || $node instanceof Param);
if (($node->flags & Modifiers::VISIBILITY_SET_MASK) === 0) { return null; }
$node->flags &= ~Modifiers::VISIBILITY_SET_MASK;
if (($node->flags & Modifiers::VISIBILITY_MASK) === 0) { $node->flags |= Modifiers::PUBLIC; }
return $node; }}İlk if koşulundaki koruma kritik önemdedir. Özellikte set görünürlüğü bayrağı yoksa kural null döndürür ve düğümü değiştirmeden bırakır. Koşulsuz dönüştüren bir kural, kendi haline bırakması gereken kodu yeniden yazardı.
Fikstürler ve test
“Fikstürler ve test” başlıklı bölümHer kuralın, kuralı .php.inc dosyalarına karşı çalıştıran ve çıktının eşleştiğini doğrulayan fikstür tabanlı bir testi vardır. Test altyapısı, Rector’ün Rector\Testing\PHPUnit\AbstractRectorTestCase sınıfına dayanır.
Test durumu küçüktür ve mevcut üç kural genelinde tutarlıdır:
<?php
declare(strict_types=1);
namespace NextPDF\Backport\Tests\Rector;
use Iterator;use PHPUnit\Framework\Attributes\DataProvider;use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class DowngradeTraitConstantsRectorTest extends AbstractRectorTestCase{ #[DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); }
public static function provideData(): Iterator { return self::yieldFilesFromDirectory(__DIR__ . '/Fixtures/DowngradeTraitConstants'); }
public function provideConfigFilePath(): string { return __DIR__ . '/config/downgrade_trait_constants.php'; }}Test, tests/Rector/config/ içinde yalnızca test edilen kuralı kaydeden, kurala özgü bir yapılandırma dosyasına işaret eder; böylece her fikstür kuralı yalıtılmış şekilde çalıştırır:
<?php
declare(strict_types=1);
use NextPDF\Backport\DowngradeTraitConstantsRector;use Rector\Config\RectorConfig;
return RectorConfig::configure() ->withRules([ DowngradeTraitConstantsRector::class, ]);Bir fikstür, .php.inc biçiminde bir dosyadır; girdiyi, bir ----- ayırıcısını ve beklenen çıktıyı içerir. Kural hiçbir değişiklik yapmadığında ayırıcıyı ve ikinci bloğu atlayın. Trait sabitleri kuralı için dönüşüm yapan bir fikstür şöyle görünür:
<?php
trait HasLimit{ private const MAX_SIZE = 1024;
public function getLimit(): int { return self::MAX_SIZE; }}?>-----<?php
trait HasLimit{ private static $MAX_SIZE = 1024;
public function getLimit(): int { return self::$MAX_SIZE; }}?>Yeni bir kuralın testlerini yazmak için:
- Yeni bir
tests/Rector/Fixtures/Downgrade<Feature>/dizini oluşturun ve her durum için bir.php.incekleyin. - Dönüştüren durumları bir
-----ayırıcısıyla, atlama durumlarını ise ayırıcısız kapsayın; örneğin değiştirilmeden geçmesi gereken, set görünürlüğü olmayan bir özelliği ekleyin. - Yalnızca kuralınızı kaydeden bir
tests/Rector/config/downgrade_<feature>.phpdosyası ekleyin. - Fikstür dizinini sağlayan ve yapılandırmaya işaret eden bir
tests/Rector/Downgrade<Feature>RectorTest.phpdosyası ekleyin. - Test paketini çalıştırın.
composer testDepo ayrıca RectorRulesBehaviorTest ve RectorRulesMetadataTest testlerini de içerir; bunlar çapraz kural davranışını doğrular ve her kuralın getRuleDefinition() yönteminin iyi biçimlendirilmiş olduğunu teyit eder. Yeni kuralınızın bu denetimlerden geçmesi için composer test komutunun tamamını çalıştırın.
Derlemeye bağlama
“Derlemeye bağlama” başlıklı bölümBir kural, ardışık düzen yapılandırmalarına kaydedilene kadar derlemede etkin değildir. İki derleme hedefinin her birinin rector/config/ içinde kendi yapılandırması vardır.
- Önce
rector/config/rector-php81.phpdosyasını açın ve kural sınıfınızı->withRules([...])listesine ekleyin. - Özelliğin PHP 7.4 çekirdek derlemesi için de geri uyarlanması gerekiyorsa, aynı sınıfı
rector/config/rector-php74.phpdosyasına ekleyin. - Mevcut girişlerle tutarlı olacak şekilde, özelliği tanıtan PHP sürümünü adlandıran bir yorum ekleyin.
<?php
declare(strict_types=1);
use NextPDF\Backport\DowngradeAsymmetricVisibilityRector;use NextPDF\Backport\DowngradeCloneWithRector;use NextPDF\Backport\DowngradeTraitConstantsRector;use Rector\Config\RectorConfig;use Rector\DowngradePhp81\Rector\Property\DowngradeReadonlyPropertyRector;
return RectorConfig::configure() ->withDowngradeSets(php81: true) ->withRules([ // PHP 8.4 — asymmetric visibility (not covered by built-in sets) DowngradeAsymmetricVisibilityRector::class,
// PHP 8.4 — clone-with syntax (not covered by built-in sets) DowngradeCloneWithRector::class,
// PHP 8.2 — trait constants (not covered by built-in sets) DowngradeTraitConstantsRector::class,
// PHP 8.1 — readonly property removal (for clone-with expansion safety) DowngradeReadonlyPropertyRector::class, ]);Derleme düzenleyicisi (scripts/build.php) kaynak depolarını birleştirir, Rector’ü bu yapılandırmalarla çalıştırır, oluşturulan composer.json dosyasını ayarlar ve çıktı üzerinde bir php -l söz dizimi denetimi çalıştırır. Kuralınıza güvenmeden önce onu PHPStan ve tam derleme ile doğrulayın.
composer analysecomposer build:dryUç durumlar ve tuzaklar
“Uç durumlar ve tuzaklar” başlıklı bölüm- Kayıt sırası önemli değildir, ancak kural sırası kavramsal olarak önemlidir. Rector’ün çok geçişli mekanizması, hiçbir kural başka bir değişiklik yapmayana kadar yeniden çalışır; bu nedenle kuralları yapılandırmada elle sıralamanız gerekmez. Yine de,
DowngradeCloneWithRectorkuralının yaptığı gibi, herhangi bir sıralama bağımlılığını sınıf belge bloğunda belgeleyin: bu kuralın genişletme işlemi, salt okunur bir özellikte başarısız olacak$clone->prop = $valüretir; bu nedenleDowngradeReadonlyPropertyRectoraynı hedef için çalışmalıdır. - Farklı türden bir düğümden yedek düğüm oluştururken boş öznitelikler geçirin.
DowngradeTraitConstantsRector, birPropertyöğesini birClassConstöğesinden oluşturur ve öznitelikler için kaynak düğümün öznitelikleri yerine[]geçirir. Özgün öznitelikleri taşırsanız, yanlış düğüm türüne birorigNodeişaretçisi bırakırsınız ve biçim koruyan yazıcıda bir doğrulamayı tetiklersiniz. - Dosya başına durumu
FileNodeziyaretinde sıfırlayın.DowngradeCloneWithRector, yalnızca her dosyanın başlangıcında geçici değişken sayacını sıfırlamak içinFileNode::classöğesinigetNodeTypes()içinde bildirir; böylece üretilen değişken adları dosyalar arasında çakışmaz. - Tam eşleşmeyi denetleyin, sonra
nulldöndürün. Bir clone-with dönüşümü, eyleme geçmeden önce çağrı adınıncloneolduğunu ve ikinci bağımsız değişkenin bir dizi sabit değeri olduğunu doğrulamalıdır; düz birclone $objkurala asla bir işlev çağrısı olarak ulaşmaz ve ikinci bağımsız değişkeni bir dizi olmayan iki bağımsız değişkenli bir çağrı kendi haline bırakılır. - Hedefin ifade edemediği değiştiricileri ayıklayın. Trait sabitleri kuralı bir sabiti statik bir özelliğe dönüştürdüğünde, görünürlüğü korur ve
staticekler, ancak PHP 8.1’de özellikler final olamayacağı için birfinaldeğiştiricisi taşımamalıdır. - Kuralı PHPStan düzey 10’da tutun. Depo,
composer analysekomutunurector/rulesvescriptsüzerinde düzey 10’da çalıştırır. Her imza için tür belirtin ve dizi şekillerini açıklamalarla tanımlayın; çözümleyiciden geçemeyecek bir kural taslak değil, kusurdur.
Ayrıca bakınız
“Ayrıca bakınız” başlıklı bölüm- Backport Builder geliştirici kılavuzu — bu kuralların çevresindeki ardışık düzen mimarisi, dal modeli ve sürüm yapıtları.
- Backport API başvurusu — backport derleme araçları için yayımlanmış API yüzeyi.
- Backport yapılandırması — derleme hedefleri ve geri uyarlama seti seçimi.
- Backport sorun giderme — oluşturulan bir geri uyarlama ağacındaki başarısızlıkların nasıl tanılanacağı.
- Rector belgeleri —
AbstractRector,RuleDefinitionvegetNodeTypes()içinde kullanılan AST düğüm sınıfları için üst akış başvurusu.