PHP Kodunu Aktarmak için En İyi Kılavuz
Yayınlanan: 2021-09-22İdeal koşullarda, tüm sitelerimiz için PHP 8.0'ı (bunu yazarken en son sürüm) kullanmalı ve yeni bir sürüm çıkar çıkmaz güncellemeliyiz. Bununla birlikte, geliştiricilerin, örneğin WordPress için genel bir eklenti oluştururken veya web sunucusunun ortamını yükseltmeyi engelleyen eski kodlarla çalışırken olduğu gibi, genellikle önceki PHP sürümleriyle çalışması gerekir.
Bu durumlarda, en son PHP kodunu kullanma umudumuzu yitirebiliriz. Ancak daha iyi bir alternatif var: Kaynak kodumuzu yine de PHP 8.0 ile yazabilir ve onu önceki bir PHP sürümüne, hatta PHP 7.1'e aktarabiliriz.
Bu kılavuzda, PHP kodunun aktarılması hakkında bilmeniz gereken her şeyi size öğreteceğiz.
Transpiling Nedir?
Aktarma, kaynak kodunu bir programlama dilinden aynı veya farklı bir programlama dilinin eşdeğer kaynak koduna dönüştürür.
Aktarma, web geliştirmede yeni bir kavram değildir: istemci tarafı geliştiriciler, JavaScript kodunun aktarıcısı olan Babel'e oldukça aşinadır.
Babel, JavaScript kodunu modern ECMAScript 2015+ sürümünden eski tarayıcılarla uyumlu eski bir sürüme dönüştürür. Örneğin, bir ES2015 ok işlevi verildiğinde:
[2, 4, 6].map((n) => n * 2);
…Babel onu ES5 versiyonuna dönüştürecek:
[2, 4, 6].map(function(n) { return n * 2; });
PHP'yi Aktarmak Nedir?
Web geliştirmede potansiyel olarak yeni olan şey, sunucu tarafı kodunun, özellikle PHP'nin aktarılması olasılığıdır.
PHP'yi aktarmak, JavaScript'i aktarmakla aynı şekilde çalışır: modern bir PHP sürümünün kaynak kodu, daha eski bir PHP sürümü için eşdeğer bir koda dönüştürülür.
Öncekiyle aynı örneği izleyerek, PHP 7.4'ten bir ok işlevi:
$nums = array_map(fn($n) => $n * 2, [2, 4, 6]);
…eşdeğer PHP 7.3 sürümüne aktarılabilir:
$nums = array_map( function ($n) { return $n * 2; }, [2, 4, 6] );
Ok işlevleri, sözdizimsel şeker oldukları için aktarılabilir, yani mevcut bir davranışı üretmek için yeni bir sözdizimi. Bu düşük asılı meyvedir.
Bununla birlikte, yeni bir davranış yaratan yeni özellikler de vardır ve bu nedenle, PHP'nin önceki sürümleri için eşdeğer bir kod olmayacaktır. PHP 8.0'da tanıtılan birleşim türlerinde durum budur:
function someFunction(float|int $param): string|float|int|null { // ... }
Bu durumlarda, yeni özellik geliştirme için gerekli olduğu, ancak üretim için gerekli olmadığı sürece aktarma hala yapılabilir. Ardından, ciddi sonuçlar olmadan özelliği aktarılan koddan tamamen kaldırabiliriz.
Böyle bir örnek sendika türleridir. Bu özellik, girdi türü ile sağlanan değer arasında herhangi bir uyumsuzluk olmadığını kontrol etmek için kullanılır, bu da hataların önlenmesine yardımcı olur. Eğer tiplerle bir çakışma varsa zaten geliştirme aşamasında olan bir hata olacaktır ve kod üretime ulaşmadan önce onu yakalayıp düzeltmeliyiz.
Bu nedenle, özelliği üretim kodundan kaldırmayı göze alabiliriz:
function someFunction($param) { // ... }
Hata üretimde devam ederse, atılan hata mesajı, birleşim türlerine sahip olduğumuzdan daha az kesin olacaktır. Ancak, bu potansiyel dezavantaj, ilk etapta birleşim tiplerinin kullanılabilmesi nedeniyle ağır basmaktadır.
PHP Kodunu Aktarmanın Avantajları
Aktarma, bir kişinin PHP'nin en son sürümünü kullanarak bir uygulamayı kodlamasını ve PHP'nin eski sürümlerini çalıştıran ortamlarda da çalışan bir sürüm oluşturmasını sağlar.
Bu, özellikle eski içerik yönetim sistemleri (CMS) için ürünler oluşturan geliştiriciler için yararlı olabilir. Örneğin WordPress, resmi olarak PHP 5.6'yı desteklemektedir (PHP 7.4+ önermesine rağmen). PHP'nin 5.6 ila 7.2 sürümlerini çalıştıran WordPress sitelerinin yüzdesi – bunların tümü Kullanım Ömrü Sonu (EOL), yani artık güvenlik güncellemeleri almıyorlar – oldukça büyük bir yüzde 34,8 ve bunun dışında herhangi bir PHP sürümünde çalışan siteler. 8.0, yüzde 99,5 gibi büyük bir oranda duruyor:

Sonuç olarak, küresel bir kitleyi hedefleyen WordPress temaları ve eklentileri, olası erişimlerini artırmak için büyük olasılıkla eski bir PHP sürümüyle kodlanacaktır. Aktarma sayesinde, bunlar PHP 8.0 kullanılarak kodlanabilir ve yine de daha eski bir PHP sürümü için yayınlanabilir, böylece mümkün olduğu kadar çok kullanıcı hedeflenebilir.
Gerçekten de, en son sürüm dışında herhangi bir PHP sürümünü desteklemesi gereken herhangi bir uygulama (şu anda desteklenen PHP sürümleri aralığında bile) yararlanabilir.
PHP 7.3 gerektiren Drupal'da durum böyledir. Aktarma sayesinde, geliştiriciler PHP 8.0 kullanarak herkese açık Drupal modülleri oluşturabilir ve bunları PHP 7.3 ile yayınlayabilir.
Başka bir örnek, PHP 8.0'ı bir nedenden dolayı kendi ortamlarında çalıştıramayan istemciler için özel kod oluştururken verilebilir. Yine de aktarma sayesinde geliştiriciler çıktılarını PHP 8.0 kullanarak kodlayabilir ve bu eski ortamlarda çalıştırabilir.
PHP Ne Zaman Aktarılmalı?
PHP kodu, önceki PHP sürümünde eşdeğeri olmayan bir PHP özelliği içermediği sürece her zaman aktarılabilir.
PHP 8.0'da tanıtılan özniteliklerle ilgili durum muhtemelen budur:
#[SomeAttr] function someFunc() {} #[AnotherAttr] class SomeClass {}
Ok işlevlerini kullanan önceki örnekte, ok işlevleri sözdizimsel şeker olduğundan kod aktarılabilir. Nitelikler, aksine, tamamen yeni davranışlar yaratır. Bu davranış PHP 7.4 ve altı ile de yeniden oluşturulabilir, ancak yalnızca manuel olarak kodlanarak, yani bir araç veya sürece dayalı olarak otomatik olarak değil (AI bir çözüm sağlayabilir, ancak henüz orada değiliz).
#[Deprecated]
gibi geliştirme kullanımına yönelik öznitelikler, birleşim türlerinin kaldırıldığı şekilde kaldırılabilir. Ancak, uygulamanın üretimdeki davranışını değiştiren nitelikler kaldırılamaz ve doğrudan aktarılamazlar.
Bugün itibariyle, hiçbir aktarıcı PHP 8.0 öznitelikleriyle kod alamaz ve eşdeğeri PHP 7.4 kodunu otomatik olarak üretemez. Sonuç olarak, PHP kodunuzun öznitelikleri kullanması gerekiyorsa, onu aktarmak zor veya olanaksız olacaktır.
Aktarılabilen PHP Özellikleri
Bunlar, şu anda aktarılabilen PHP 7.1 ve üzeri özelliklerdir. Kodunuz yalnızca bu özellikleri kullanıyorsa, aktarılan uygulamanızın çalışacağından emin olabilirsiniz. Aksi takdirde, aktarılan kodun hata üretip üretmeyeceğini değerlendirmeniz gerekir.
PHP Sürümü | Özellikler |
---|---|
7.1 | Her şey |
7.2 | – object türü– parametre tipi genişletme – preg_match içinde PREG_UNMATCHED_AS_NULL bayrağı |
7.3 | – list() / dizi yıkımındaki referans atamaları ( foreach içi hariç — #4376)– Esnek Heredoc ve Nowdoc sözdizimi – İşlev çağrılarında sondaki virgül – set(raw)cookie $seçenek argümanını kabul eder |
7.4 | – Yazılan özellikler – Ok fonksiyonları – Boş birleştirme atama operatörü – Dizilerin içinde paketin açılması – Sayısal değişmez ayırıcı – etiket isimleri dizisi ile strip_tags() – kovaryant dönüş türleri ve kontravariant param türleri |
8.0 | – Birlik türleri – mixed sözde tip– static dönüş tipi– ::class sihirli sabiti- ifadeleri match – istisnaları yalnızca türe göre catch – Sıfır güvenli operatör – Sınıf kurucu mülk promosyonu – Parametre listelerinde ve kapatma use listelerinde sondaki virgüller |
PHP Aktarıcıları
Şu anda PHP kodunu aktarmak için bir araç var: Rector.
Rector, PHP kodunu programlanabilir kurallara göre dönüştüren bir PHP yeniden yapılandırma aracıdır. Kaynak kodu ve çalıştırılacak kurallar kümesini giriyoruz ve Rektör kodu dönüştürecek.
Rektör, Composer üzerinden projeye yüklenen komut satırı üzerinden çalıştırılır. Yürütüldüğünde, Rektör, dönüştürmeden önce ve sonra kodun bir "farkını" (eklemeler yeşil, çıkarmalar kırmızı) verir:

PHP'nin Hangi Sürümüne Aktarılacak
Kodu PHP sürümleri arasında aktarmak için ilgili kurallar oluşturulmalıdır.
Bugün, Rector kitaplığı, PHP 8.0 ila 7.1 aralığında kod aktarma kurallarının çoğunu içerir. Bu nedenle, PHP kodumuzu 7.1 sürümüne kadar güvenilir bir şekilde aktarabiliriz.
PHP 7.1'den 7.0'a ve 7.0'dan 5.6'ya aktarım için de kurallar vardır, ancak bunlar ayrıntılı değildir. Bunları tamamlamak için çalışmalar devam ediyor, bu nedenle sonunda PHP kodunu 5.6 sürümüne aktarabiliriz.
Aktarma vs Backporting
Backporting, aktarmaya benzer, ancak daha basittir. Backporting kodu, mutlaka bir dildeki yeni özelliklere dayanmaz. Bunun yerine, aynı işlevsellik, dilin yeni sürümünden ilgili kodu kopyalayarak/yapıştırarak/uyarlayarak dilin eski bir sürümüne sağlanabilir.
Örneğin, str_contains
işlevi PHP 8.0'da tanıtıldı. PHP 7.4 ve altı için aynı işlev şu şekilde kolayca uygulanabilir:
if (!defined('PHP_VERSION_ID') || (defined('PHP_VERSION_ID') && PHP_VERSION_ID < 80000)) { if (!function_exists('str_contains')) { /** * Checks if a string contains another * * @param string $haystack The string to search in * @param string $needle The string to search * @return boolean Returns TRUE if the needle was found in haystack, FALSE otherwise. */ function str_contains(string $haystack, string $needle): bool { return strpos($haystack, $needle) !== false; } } }
Backporting aktarmadan daha basit olduğu için, backporting işi ne zaman yaparsa yapsın bu çözümü seçmeliyiz.
PHP 8.0 ile 7.1 arasındaki aralıkla ilgili olarak, Symfony'nin çoklu doldurma kitaplıklarını kullanabiliriz:
- Çoklu Doldurma PHP 7.1
- Çoklu Doldurma PHP 7.2
- Çoklu Doldurma PHP 7.3
- Çoklu Doldurma PHP 7.4
- Çoklu Doldurma PHP 8.0
Bu kitaplıklar aşağıdaki işlevleri, sınıfları, sabitleri ve arabirimleri destekler:
PHP Sürümü | Özellikler |
---|---|
7.2 | Fonksiyonlar:
sabitler:
|
7.3 | Fonksiyonlar:
İstisnalar:
|
7.4 | Fonksiyonlar:
|
8.0 | Arayüzler:
Sınıflar:
sabitler:
Fonksiyonlar:
|
Aktarılan PHP Örnekleri
Aktarılan PHP kodunun birkaç örneğini ve tamamen aktarılan birkaç paketi inceleyelim.
PHP Kodu
match
ifadesi PHP 8.0'da tanıtıldı. Bu kaynak kodu:
function getFieldValue(string $fieldName): ?string { return match($fieldName) { 'foo' => 'foofoo', 'bar' => 'barbar', 'baz' => 'bazbaz', default => null, }; }
… switch
operatörü kullanılarak eşdeğer PHP 7.4 sürümüne aktarılacaktır:
function getFieldValue(string $fieldName): ?string { switch ($fieldName) { case 'foo': return 'foofoo'; case 'bar': return 'barbar'; case 'baz': return 'bazbaz'; default: return null; } }
nullsafe operatörü PHP 8.0'da da tanıtıldı:
public function getValue(TypeResolverInterface $typeResolver): ?string { return $this->getResolver($typeResolver)?->getValue(); }
İşlemin iki kez yürütülmesini önlemek için, aktarılan kodun önce işlemin değerini yeni bir değişkene ataması gerekir:
public function getValue(TypeResolverInterface $typeResolver): ?string { return ($val = $this->getResolver($typeResolver)) ? $val->getValue() : null; }
PHP 8.0'da da tanıtılan yapıcı özelliği yükseltme özelliği, geliştiricilerin daha az kod yazmasına olanak tanır:
class QueryResolver { function __construct(protected QueryFormatter $queryFormatter) { } }
PHP 7.4 için aktarırken, tam kod parçası üretilir:
class QueryResolver { protected QueryFormatter $queryFormatter; function __construct(QueryFormatter $queryFormatter) { $this->queryFormatter = $queryFormatter; } }
Yukarıdaki aktarılan kod, PHP 7.4'te tanıtılan, yazılan özellikleri içerir. Bu kodu PHP 7.3'e aktarmak, onları docblock'larla değiştirir:
class QueryResolver { /** * @var QueryFormatter */ protected $queryFormatter; function __construct(QueryFormatter $queryFormatter) { $this->queryFormatter = $queryFormatter; } }
PHP Paketleri
Aşağıdaki kitaplıklar üretim için aktarılıyor:
kitaplık/açıklama | kod/notlar |
---|---|
rektör Aktarmayı mümkün kılan PHP yeniden yapılandırma aracı | - Kaynak kodu – Aktarılan kod – Notlar |
Kolay Kodlama Standartları PHP kodunun bir dizi kurala uymasını sağlayan araç | - Kaynak kodu – Aktarılan kod – Notlar |
WordPress için GraphQL API'si WordPress için bir GraphQL sunucusu sağlayan eklenti | - Kaynak kodu – Aktarılan kod – Notlar |
PHP'yi Aktarmanın Artıları ve Eksileri
PHP'yi aktarmanın faydası daha önce açıklanmıştır: kaynak kodun PHP 8.0'ı (yani PHP'nin en son sürümü) kullanmasına izin verir; bu, üretimin eski bir uygulama veya ortamda çalışması için PHP için daha düşük bir sürüme dönüştürülecektir.
Bu, etkili bir şekilde daha iyi geliştiriciler olmamızı ve daha yüksek kalitede kod üretmemizi sağlar. Bunun nedeni, kaynak kodumuzun PHP 8.0'ın birleşim türlerini, PHP 7.4'ün yazılan özelliklerini ve PHP'nin her yeni sürümüne eklenen farklı türleri ve sözde türleri (PHP 8.0'dan mixed
, PHP 7.2'den object
) kullanabilmesidir. PHP'nin diğer modern özellikleri.
Bu özellikleri kullanarak geliştirme sırasında hataları daha iyi yakalayabilir ve okunması daha kolay kod yazabiliriz.
Şimdi dezavantajlarına bir göz atalım.
Kodlanmalı ve Bakımı Yapılmalıdır
Rektör kodu otomatik olarak aktarabilir, ancak işlem, bizim özel kurulumumuzla çalışması için büyük olasılıkla bazı manuel girdiler gerektirecektir.
Üçüncü Taraf Kitaplıkları da Aktarılmalıdır
Bu, olası nedeni bulmak için kaynak kodlarını araştırmamız gerektiğinden, bunları aktarma hatalar ürettiğinde bir sorun haline gelir. Sorun çözülebilirse ve proje açık kaynak ise, bir çekme isteği göndermemiz gerekecek. Kütüphane açık kaynak değilse, bir barikat ile karşılaşabiliriz.
Kod Aktarılamadığında Rektör Bize Haber Vermiyor
Kaynak kodu PHP 8.0 öznitelikleri veya aktarılamayan başka bir özellik içeriyorsa, devam edemeyiz. Ancak Rektör bu durumu kontrol etmeyecek, bu yüzden manuel olarak yapmamız gerekiyor. Bu, kendi kaynak kodumuzla ilgili olarak büyük bir sorun olmayabilir, çünkü buna zaten aşinayız, ancak üçüncü taraf bağımlılıkları için bir engel haline gelebilir.
Hata Ayıklama Bilgileri Kaynak Kodu Değil, Aktarılan Kodu Kullanır
Uygulama, üretimde yığın izleme içeren bir hata mesajı ürettiğinde, satır numarası aktarılan kodu gösterecektir. Kaynak kodda karşılık gelen satır numarasını bulmak için aktarılandan orijinal koda geri dönüştürmemiz gerekiyor.
Aktarılan Kodun Öneki de Yazılmalıdır
Aktarılan projemiz ve ayrıca üretim ortamında kurulan diğer bazı kitaplıklar, aynı üçüncü taraf bağımlılığını kullanabilir. Bu üçüncü taraf bağımlılığı projemiz için aktarılacak ve orijinal kaynak kodunu diğer kitaplık için koruyacak. Bu nedenle, olası çakışmaları önlemek için aktarılan sürüme PHP-Scoper, Strauss veya başka bir araç aracılığıyla ön ek eklenmelidir.
Aktarma Sürekli Entegrasyon (CI) Sırasında Gerçekleşmeli
Aktarılan kod, doğal olarak kaynak kodu geçersiz kılacağından, aktarma işlemini geliştirme bilgisayarlarımızda çalıştırmamalıyız, aksi takdirde yan etkiler oluşturma riskiyle karşı karşıya kalırız. İşlemi bir CI çalıştırması sırasında çalıştırmak daha uygundur (aşağıda daha fazlası).
PHP Nasıl Aktarılır
İlk olarak, geliştirme için projemizde Rektörü kurmamız gerekiyor:
composer require rector/rector --dev
Ardından, gerekli kural setlerini içeren projenin kök dizininde bir rector.php
yapılandırma dosyası oluşturuyoruz. Kodu PHP 8.0'dan 7.1'e düşürmek için bu yapılandırmayı kullanıyoruz:
use Rector\Set\ValueObject\DowngradeSetList; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $containerConfigurator->import(DowngradeSetList::PHP_80); $containerConfigurator->import(DowngradeSetList::PHP_74); $containerConfigurator->import(DowngradeSetList::PHP_73); $containerConfigurator->import(DowngradeSetList::PHP_72); };
İşlemin beklendiği gibi yürütüldüğünden emin olmak için, işlenecek konumu/konumları ileterek (bu durumda, src/
klasörü altındaki tüm dosyalar) Rektörün process
komutunu kuru modda çalıştırabiliriz:

vendor/bin/rector process src --dry-run
Aktarmayı gerçekleştirmek için, dosyaları mevcut konumlarında değiştirecek olan Rector'un process
komutunu çalıştırıyoruz:
vendor/bin/rector process src
Lütfen dikkat: geliştirme bilgisayarlarımızda rector process
çalıştırırsak, kaynak kodu src/
altında yerinde dönüştürülür. Ancak, kodu düşürürken kaynak kodu geçersiz kılmamak için dönüştürülen kodu farklı bir konumda üretmek istiyoruz. Bu nedenle, sürekli entegrasyon sırasında süreci çalıştırmak en uygunudur.
Aktarma Sürecini Optimize Etme
Üretim için aktarılmış bir teslimat oluşturmak için yalnızca üretim kodunun dönüştürülmesi gerekir; sadece geliştirme için gerekli olan kod atlanabilir. Bu, tüm testleri (hem projemiz hem de bağımlılıkları için) ve geliştirme için tüm bağımlılıkları aktarmaktan kaçınabileceğimiz anlamına gelir.
Testlerle ilgili olarak, projemiz için olanların nerede olduğunu zaten bileceğiz - örneğin, tests/
klasörü altında. Ayrıca, bağımlılıklar için olanların nerede olduğunu da bulmalıyız - örneğin, alt klasörleri olan tests/
, test/
ve Test/
(farklı kitaplıklar için). Ardından, Rektöre şu klasörleri işlemeyi atlamasını söyleriz:
return static function (ContainerConfigurator $containerConfigurator): void { // ... $parameters->set(Option::SKIP, [ // Skip tests '*/tests/*', '*/test/*', '*/Test/*', ]); };
Bağımlılıklarla ilgili olarak, Composer hangilerinin geliştirme için olduğunu ( composer.json
require-dev
girişi altındakiler) ve hangilerinin üretim için olduğunu (giriş altındakiler require
) bilir.
Composer'dan üretim için tüm bağımlılıkların yollarını almak için şunu çalıştırırız:
composer info --path --no-dev
Bu komut, adları ve yolları ile aşağıdaki gibi bir bağımlılık listesi üretecektir:
brain/cortex /Users/leo/GitHub/leoloso/PoP/vendor/brain/cortex composer/installers /Users/leo/GitHub/leoloso/PoP/vendor/composer/installers composer/semver /Users/leo/GitHub/leoloso/PoP/vendor/composer/semver guzzlehttp/guzzle /Users/leo/GitHub/leoloso/PoP/vendor/guzzlehttp/guzzle league/pipeline /Users/leo/GitHub/leoloso/PoP/vendor/league/pipeline
Tüm yolları ayıklayabilir ve bunları projemizin src/
klasörünü ve üretim için tüm bağımlılıkları içeren klasörleri işleyecek olan Rector komutuna besleyebiliriz:
$ paths="$(composer info --path --no-dev | cut -d' ' -f2- | sed 's/ //g' | tr '\n' ' ')" $ vendor/bin/rector process src $paths
Başka bir iyileştirme, Rector'un halihazırda hedef PHP sürümünü kullanan bu bağımlılıkları işlemesini engelleyebilir. Bir kitaplık PHP 7.1 (veya aşağıdaki herhangi bir sürüm) ile kodlanmışsa, onu PHP 7.1'e aktarmaya gerek yoktur.
Bunu başarmak için PHP 7.2 ve üstü gerektiren kitaplıkların listesini alabilir ve yalnızca bunları işleyebiliriz. Tüm bu kitaplıkların adlarını Composer'ın Why why-not
komutuyla şu şekilde elde edeceğiz:
composer why-not php "7.1.*" | grep -o "\S*\/\S*"
Bu komut --no-dev
bayrağıyla çalışmadığından, yalnızca üretim için bağımlılıkları dahil etmek için, önce geliştirme bağımlılıklarını kaldırmamız ve otomatik yükleyiciyi yeniden oluşturmamız, komutu yürütmemiz ve sonra bunları yeniden eklememiz gerekir:
$ composer install --no-dev $ packages=$(composer why-not php "7.1.*" | grep -o "\S*\/\S*") $ composer install
Composer'ın info --path
komutu, bir paketin yolunu şu biçimde alır:
# Executing this command $ composer info psr/cache --path # Produces this response: psr/cache /Users/leo/GitHub/leoloso/PoP/vendor/psr/cache
Aktarılacak tüm yolları elde etmek için listemizdeki tüm öğeler için bu komutu uygularız:
for package in $packages do path=$(composer info $package --path | cut -d' ' -f2-) paths="$paths $path" done
Son olarak, bu listeyi Rektör'e sunuyoruz (artı projenin src/
klasörü):
Size rekabet avantajı sağlayan bir barındırma çözümüne mi ihtiyacınız var? Kinsta sizi inanılmaz hız, son teknoloji güvenlik ve otomatik ölçeklendirme ile donattı. Planlarımıza göz atın
vendor/bin/rector process src $paths
Kodu Aktarırken Kaçınılması Gereken Tuzaklar
Kod aktarma, genellikle projeye özel ince ayarlar gerektiren bir sanat olarak kabul edilebilir. Karşılaşabileceğimiz birkaç soruna bakalım.
Zincirleme Kurallar Her Zaman İşlenmez
Zincirleme kural, bir kuralın önceki bir kural tarafından üretilen kodu dönüştürmesi gerektiği zamandır.
Örneğin, symfony/cache
kitaplığı şu kodu içerir:
final class CacheItem implements ItemInterface { public function tag($tags): ItemInterface { // ... return $this; } }
PHP 7.4'ten 7.3'e aktarırken, işlev tag
iki değişikliğe uğramalıdır:
-
ItemInterface
dönüş türü,DowngradeCovariantReturnTypeRector
kuralı nedeniyle önceself
dönüştürülmelidir. - Daha sonra,
DowngradeSelfTypeDeclarationRector
kuralı nedeniyleself
dönüş türü kaldırılmalıdır.
Nihai sonuç şu olmalıdır:
final class CacheItem implements ItemInterface { public function tag($tags) { // ... return $this; } }
Ancak, Rektör yalnızca ara aşamanın çıktısını verir:
final class CacheItem implements ItemInterface { public function tag($tags): self { // ... return $this; } }
Sorun şu ki, Rektör her zaman kuralların uygulanma sırasını kontrol edemez.
Çözüm, hangi zincirleme kuralların işlenmeden bırakıldığını belirlemek ve bunları uygulamak için yeni bir Rektör çalıştırması yürütmektir.
Zincirleme kuralları tanımlamak için kaynak kodunda iki kez Rector çalıştırıyoruz, şöyle:
$ vendor/bin/rector process src $ vendor/bin/rector process src --dry-run
İlk defa, aktarmayı yürütmek için beklendiği gibi Rektör'ü çalıştırıyoruz. İkinci kez, yapılacak değişiklikler olup olmadığını keşfetmek için --dry-run
bayrağını kullanırız. Varsa, komut bir hata koduyla çıkacaktır ve “diff” çıktısı hangi kuralın/kuralların hala uygulanabileceğini gösterecektir. Bu, bazı zincirleme kuralların işlenmediği ilk çalıştırmanın tamamlanmadığı anlamına gelir.

Uygulanmamış zincirleme kuralı (veya kuralları) belirledikten sonra, başka bir Rector yapılandırma dosyası oluşturabiliriz - örneğin, rector-chained-rule.php
eksik kuralı yürütecektir. src/
altındaki tüm dosyalar için tam bir kural kümesini işlemek yerine, bu sefer, uygulanması gereken belirli dosyada belirli eksik kuralı çalıştırabiliriz:
// rector-chained-rule.php use Rector\Core\Configuration\Option; use Rector\DowngradePhp74\Rector\ClassMethod\DowngradeSelfTypeDeclarationRector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $services->set(DowngradeSelfTypeDeclarationRector::class); $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PATHS, [ __DIR__ . '/vendor/symfony/cache/CacheItem.php', ]); };
Son olarak, ikinci geçişinde Rector'a input --config
aracılığıyla yeni yapılandırma dosyasını kullanmasını söyleriz:
# First pass with all modifications $ vendor/bin/rector process src # Second pass to fix a specific problem $ vendor/bin/rector process --config=rector-chained-rule.php
Besteci Bağımlılıkları Tutarsız Olabilir
Kitaplıklar, geliştirme için planlanacak bir bağımlılık bildirebilir (yani, composer.json
içindeki request require-dev
altında), ancak yine de, üretim için onlardan bazı kodlara başvururlar (örneğin, src/
altındaki bazı dosyalarda, tests/
değil).
Genellikle, bu bir sorun değildir çünkü bu kod üretime yüklenmeyebilir, bu nedenle uygulamada asla bir hata olmaz. Ancak, Rektör kaynak kodu ve bağımlılıklarını işlerken, başvurulan tüm kodların yüklenebileceğini doğrular. Herhangi bir dosya, kurulu olmayan bir kitaplıktan bir kod parçasına başvurursa, Rektör bir hata verir (çünkü yalnızca geliştirme için gerekli olduğu bildirilmiş).
Örneğin, Symfony'nin Cache bileşeninden EarlyExpirationHandler
sınıfı, Messenger bileşeninden MessageHandlerInterface
arabirimini uygular:
class EarlyExpirationHandler implements MessageHandlerInterface { //... }
Ancak, symfony/cache
, symfony/messenger
geliştirme için bir bağımlılık olduğunu bildirir. Ardından, symfony/cache
bağlı bir projede Rector çalıştırıldığında bir hata verir:
[ERROR] Could not process "vendor/symfony/cache/Messenger/EarlyExpirationHandler.php" file, due to: "Analyze error: "Class Symfony\Component\Messenger\Handler\MessageHandlerInterface not found.". Include your files in "$parameters->set(Option::AUTOLOAD_PATHS, [...]);" in "rector.php" config. See https://github.com/rectorphp/rector#configuration".
Bu sorunun üç çözümü var:
- Rector yapılandırmasında, o kod parçasına başvuran dosyayı işlemeyi atlayın:
return static function (ContainerConfigurator $containerConfigurator): void { // ... $parameters->set(Option::SKIP, [ __DIR__ . '/vendor/symfony/cache/Messenger/EarlyExpirationHandler.php', ]); };
- Eksik kitaplığı indirin ve Rektör tarafından otomatik olarak yüklenecek yolunu ekleyin:
return static function (ContainerConfigurator $containerConfigurator): void { // ... $parameters->set(Option::AUTOLOAD_PATHS, [ __DIR__ . '/vendor/symfony/messenger', ]); };
- Projenizin üretim için eksik kitaplığa bağlı olmasını sağlayın:
composer require symfony/messenger
Aktarma ve Sürekli Entegrasyon
Daha önce bahsedildiği gibi, geliştirme bilgisayarlarımızda --dry-run
bayrağını kullanmalıyız, aksi takdirde kaynak kodu aktarılan kodla geçersiz kılınır. Bu nedenle, süreci yürütmek için geçici koşucuları döndürebileceğimiz sürekli entegrasyon (CI) sırasında gerçek aktarma sürecini çalıştırmak daha uygundur.
Aktarma sürecini yürütmek için ideal bir zaman, projemiz için yayın oluştururken. Örneğin, aşağıdaki kod, bir WordPress eklentisinin sürümünü oluşturan GitHub Eylemleri için bir iş akışıdır:
name: Generate Installable Plugin and Upload as Release Asset on: release: types: [published] jobs: build: name: Build, Downgrade and Upload Release runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/[email protected] - name: Downgrade code for production (to PHP 7.1) run: | composer install vendor/bin/rector process sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php - name: Build project for production run: | composer install --no-dev --optimize-autoloader mkdir build - name: Create artifact uses: montudor/[email protected] with: args: zip -X -r build/graphql-api.zip . -x *.git* node_modules/\* .* "*/\.*" CODE_OF_CONDUCT.md CONTRIBUTING.md ISSUE_TEMPLATE.md PULL_REQUEST_TEMPLATE.md rector.php *.dist composer.* dev-helpers** build** - name: Upload artifact uses: actions/[email protected] with: name: graphql-api path: build/graphql-api.zip - name: Upload to release uses: JasonEtco/[email protected] with: args: build/graphql-api.zip application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Bu iş akışı, GitHub Eylemleri aracılığıyla bir WordPress eklentisini yayınlamak için standart bir prosedür içerir. Eklenti kodunu PHP 7.4'ten 7.1'e dönüştürmek için yapılan yeni ekleme bu adımda gerçekleşir:
- name: Downgrade code for production (to PHP 7.1) run: | vendor/bin/rector process sed -i 's/Requires PHP: 7.4/Requires PHP: 7.1/' graphql-api.php
Birlikte ele alındığında, bu iş akışı şimdi aşağıdaki adımları gerçekleştirir:
- PHP 7.4 ile yazılmış bir WordPress eklentisinin kaynak kodunu deposundan kontrol eder
- Composer bağımlılıklarını yükler
- Kodunu PHP 7.4'ten 7.1'e aktarır
- Eklentinin ana dosyasının başlığındaki "PHP gerektirir" girişini
"7.4"
ten"7.1"
e değiştirir - Geliştirme için gereken bağımlılıkları kaldırır
- Tüm gereksiz dosyalar hariç tutularak eklentinin .zip dosyasını oluşturur
- .zip dosyasını bir yayın varlığı olarak (ve ayrıca GitHub Eylemine bir yapıt olarak) yükler
Aktarılan Kodun Test Edilmesi
Kod PHP 7.1'e aktarıldıktan sonra, bunun iyi çalıştığını nasıl bilebiliriz? Veya başka bir deyişle, tamamen dönüştürüldüğünü ve PHP kodunun daha yüksek sürümlerinin hiçbir kalıntısının geride kalmadığını nereden biliyoruz?
Kodu aktarmaya benzer şekilde, çözümü bir CI işlemi içinde uygulayabiliriz. Buradaki fikir, koşucunun ortamını PHP 7.1 ile kurmak ve aktarılan kod üzerinde bir linter çalıştırmaktır. Herhangi bir kod parçası PHP 7.1 ile uyumlu değilse (PHP 7.4'ten dönüştürülmemiş, yazılan bir özellik gibi), o zaman linter bir hata verecektir.
PHP için iyi çalışan bir linter, PHP Parallel Lint'tir. Bu kitaplığı projemizde geliştirme bağımlılığı olarak kurabilir veya CI sürecinin bağımsız bir Besteci projesi olarak kurmasını sağlayabiliriz:
composer create-project php-parallel-lint/php-parallel-lint
Kod PHP 7.2 ve üzerini içerdiğinde, PHP Parallel Lint şuna benzer bir hata verir:
Run php-parallel-lint/parallel-lint layers/ vendor/ --exclude vendor/symfony/polyfill-ctype/bootstrap80.php --exclude vendor/symfony/polyfill-intl-grapheme/bootstrap80.php --exclude vendor/symfony/polyfill-intl-idn/bootstrap80.php --exclude vendor/symfony/polyfill-intl-normalizer/bootstrap80.php --exclude vendor/symfony/polyfill-mbstring/bootstrap80.php PHP 7.1.33 | 10 parallel jobs ............................................................ 60/2870 (2 %) ............................................................ 120/2870 (4 %) ... ............................................................ 660/2870 (22 %) .............X.............................................. 720/2870 (25 %) ............................................................ 780/2870 (27 %) ... ............................................................ 2820/2870 (98 %) .................................................. 2870/2870 (100 %) Checked 2870 files in 15.4 seconds Syntax error found in 1 file ------------------------------------------------------------ Parse error: layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php:55 53| '0.8.0', 54| \__('GraphQL API for WordPress', 'graphql-api'), > 55| ))) { 56| $plugin->setup(); 57| } Unexpected ')' in layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php on line 55 Error: Process completed with exit code 1.
CI'mizin iş akışına linter ekleyelim. PHP 8.0'dan 7.1'e kod aktarmak ve test etmek için yürütülecek adımlar şunlardır:
- Kaynak kodunu kontrol edin
- Rektörün kaynak kodunu yorumlayabilmesi için ortamın PHP 8.0'ı çalıştırmasını sağlayın
- Kodu PHP 7.1'e aktarın
- PHP linter aracını kurun
- Ortamın PHP sürümünü 7.1 olarak değiştirin
- Aktarılan kodda linter'i çalıştırın
Bu GitHub Eylem iş akışı işi yapar:
name: Downgrade PHP tests jobs: main: name: Downgrade code to PHP 7.1 via Rector, and execute tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/[email protected] - name: Set-up PHP uses: shivammathur/[email protected] with: php-version: 8.0 coverage: none - name: Local packages - Downgrade PHP code via Rector run: | composer install vendor/bin/rector process # Prepare for testing on PHP 7.1 - name: Install PHP Parallel Lint run: composer create-project php-parallel-lint/php-parallel-lint --ansi - name: Switch to PHP 7.1 uses: shivammathur/[email protected] with: php-version: 7.1 coverage: none # Lint the transpiled code - name: Run PHP Parallel Lint on PHP 7.1 run: php-parallel-lint/parallel-lint src/ vendor/ --exclude vendor/symfony/polyfill-ctype/bootstrap80.php --exclude vendor/symfony/polyfill-intl-grapheme/bootstrap80.php --exclude vendor/symfony/polyfill-intl-idn/bootstrap80.php --exclude vendor/symfony/polyfill-intl-normalizer/bootstrap80.php --exclude vendor/symfony/polyfill-mbstring/bootstrap80.php
Lütfen Symfony'nin çoklu doldurma kitaplıklarından (aktarılması gerekmeyen) birkaç bootstrap80.php
dosyasının linterden çıkarılması gerektiğine dikkat edin. Bu dosyalar PHP 8.0 içerir, bu nedenle linter bunları işlerken hata verir. Ancak, üretime yalnızca PHP 8.0 veya üzeri çalıştırıldığında yükleneceklerinden bu dosyaları hariç tutmak güvenlidir:
if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; }
Özet
Bu makale bize PHP kodumuzu nasıl aktaracağımızı öğreterek kaynak kodda PHP 8.0 kullanmamıza ve PHP 7.1 üzerinde çalışan bir sürüm oluşturmamıza izin verdi. Aktarma, bir PHP yeniden yapılandırma aracı olan Rector aracılığıyla yapılır.
Geliştirme sırasındaki hataları daha iyi yakalayabileceğimiz ve doğal olarak okunması ve anlaşılması daha kolay kodlar üretebileceğimiz için kodumuzu aktarmak bizi daha iyi geliştiriciler yapar.
Aktarma ayrıca, kodumuzu CMS'den belirli PHP gereksinimleriyle ayırmamızı sağlar. Artık, kullanıcı tabanımızı ciddi şekilde kısıtlamadan herkese açık bir WordPress eklentisi veya Drupal modülü oluşturmak için PHP'nin en son sürümünü kullanmak istiyorsak bunu yapabiliriz.
PHP'yi aktarmakla ilgili herhangi bir sorunuz var mı? Yorumlar bölümünde bize bildirin!