Yeni bir Laravel uygulaması yüklediniz, başlattınız ve hoş geldiniz sayfasını aldınız. Herkes gibi siz de bunun nasıl oluşturulduğunu görmeye çalışıyorsunuz, bu nedenle dosyasına atlıyorsunuz ve şu kodla karşılaşıyorsunuz: web.php <?php use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); Hoş geldiniz görünümünü nasıl elde ettiğimiz açık, ancak Laravel'in yönlendiricisinin nasıl çalıştığını merak ediyorsunuz, bu yüzden kodun içine dalmaya karar veriyorsunuz. İlk varsayım şudur: Üzerinde statik yöntemini çağırdığımız bir sınıfı var. Ancak tıklandığında orada yöntemi yoktur. Peki ne tür bir kara büyü oluyor? Hadi bunu aydınlatalım! get() Route get() Düzenli Cepheler Lütfen PHPDoc'ların çoğunu çıkardığımı ve yalnızca basitlik amacıyla türleri satır içi yazdığımı unutmayın; "..." daha fazla kodu ifade eder. Herhangi bir karışıklığı önlemek için IDE'nizi açmanızı ve kodu takip etmenizi şiddetle tavsiye ederim. Örneğimizi takip ederek sınıfını inceleyelim. Route <?php namespace Illuminate\Support\Facades; class Route extends Facade { // ... protected static function getFacadeAccessor(): string { return 'router'; } } Burada fazla bir şey yok, yalnızca dizesini döndüren yöntemi var. Bunu aklımızda tutarak ebeveyn sınıfına geçelim. router getFacadeAccessor() <?php namespace Illuminate\Support\Facades; use RuntimeException; // ... abstract class Facade { // ... public static function __callStatic(string $method, array $args): mixed { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); } } Ebeveyn sınıfında pek çok yöntem vardır, ancak yöntemi yoktur. Ancak ilginç bir yöntem var: yöntemi. Bu, bizim durumumuzda gibi tanımsız bir statik yöntem çağrıldığında çağrılan bir yöntemdir. Bu nedenle, çağrımız çağırırken ilettiğimiz şeyi, rotayı ve hoş geldiniz görünümünü döndüren temsil eder. get() __callStatic() get() sihirli __callStatic('get', ['/', Closure()]) Route::get() / Closure() tetiklendiğinde, önce öğesini çağırarak bir değişkeni ayarlamaya çalışır, çağrının iletilmesi gereken gerçek sınıfı tutar, daha yakından bakalım, birazdan anlamlı olacaktır __callStatic() getFacadeRoot() $instance $instance // Facade.php public static function getFacadeRoot() { return static::resolveFacadeInstance(static::getFacadeAccessor()); } Hey, bakın bu alt sınıfındaki yöntemidir ve bunun dizesini döndürdüğünü biliyoruz. Bu dizesi daha sonra, "Bu dize hangi sınıfı temsil ediyor?" diyen bir tür eşleme olan bir sınıfa çözümlemeye çalışan a iletilir. Görelim. Route getFacadeAccessor() router router resolveFacadeInstance() // Facade.php protected static function resolveFacadeInstance($name) { if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } if (static::$app) { if (static::$cached) { return static::$resolvedInstance[$name] = static::$app[$name]; } return static::$app[$name]; } } Öncelikle adlı statik bir dizinin verilen (yine ) ile ayarlanmış bir değere sahip olup olmadığını kontrol eder. Bir eşleşme bulursa yalnızca bu değeri döndürür. Bu, performansı biraz optimize etmek için Laravel önbelleğe alma işlemidir. Bu önbelleğe alma tek bir istekte gerçekleşir. Bu yöntem aynı istek içinde aynı argümanla birden çok kez çağrılırsa önbelleğe alınan değeri kullanır. Bunun ilk çağrı olduğunu varsayalım ve devam edelim. $resolvedInstance $name router Daha sonra ayarlanıp ayarlanmadığını ve uygulama kapsayıcısının bir örneği olup olmadığını kontrol eder. $app $app // Facade.php protected static \Illuminate\Contracts\Foundation\Application $app; Uygulama kapsayıcısının ne olduğunu merak ediyorsanız onu sınıflarınızın depolandığı bir kutu olarak düşünün. Bu derslere ihtiyacınız olduğunda o kutuya uzanmanız yeterli. Bazen bu kap biraz sihir gerçekleştirir. Kutu boş olsa ve bir ders almak için uzansanız bile, onu sizin için alacaktır. Bu başka bir makalenin konusu. Şimdi, " ne zaman ayarlanır?" diye merak edebilirsiniz, çünkü öyle olması gerekiyor, aksi halde sahip olmayacağız. Bu uygulama kapsayıcısı, uygulamamızın önyükleme işlemi sırasında ayarlanır. sınıfına hızlıca bir göz atalım: $app $instance \Illuminate\Foundation\Http\Kernel <?php namespace Illuminate\Foundation\Http; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Facade; use Illuminate\Contracts\Http\Kernel as KernelContract; // ... class Kernel implements KernelContract { // ... protected $app; protected $bootstrappers = [ \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, // <- this guy \Illuminate\Foundation\Bootstrap\RegisterProviders::class, \Illuminate\Foundation\Bootstrap\BootProviders::class, ]; public function bootstrap(): void { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } } } Bir istek geldiğinde yönlendiriciye gönderilir. Bundan hemen önce, uygulamayı hazırlamak için dizisini kullanan yöntemi çağrılır. sınıfında yöntemini keşfederseniz, bu önyükleyiciler arasında yinelenir ve onların yöntemini çağırır. bootstrappers bootstrap() \Illuminate\Foundation\Application bootstrapWith() bootstrap() Basit olması açısından, içinde çağrılacak bir yöntemi içerdiğini bildiğimiz öğesine odaklanalım. bootstrapWith() bootstrap() \Illuminate\Foundation\Bootstrap\RegisterFacades <?php namespace Illuminate\Foundation\Bootstrap; use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\AliasLoader; use Illuminate\Foundation\PackageManifest; use Illuminate\Support\Facades\Facade; class RegisterFacades { // ... public function bootstrap(Application $app): void { Facade::clearResolvedInstances(); Facade::setFacadeApplication($app); // Interested here AliasLoader::getInstance(array_merge( $app->make('config')->get('app.aliases', []), $app->make(PackageManifest::class)->aliases() ))->register(); } } Ve işte burada, setFacadeApplication() statik yöntemini kullanarak uygulama konteynerini sınıfına ayarlıyoruz Facade setFacadeApplication(). // RegisterFacades.php public static function setFacadeApplication($app) { static::$app = $app; } Bakın, içinde test ettiğimiz özelliğini atadık. Bu sorunun cevabını veriyor; devam edelim. resolveFacadeInstance() $app // Facade.php protected static function resolveFacadeInstance($name) { if (isset(static::$resolvedInstance[$name])) { return static::$resolvedInstance[$name]; } if (static::$app) { if (static::$cached) { return static::$resolvedInstance[$name] = static::$app[$name]; } return static::$app[$name]; } } Uygulama önyüklemesi sırasında ayarlandığını doğruladık. Bir sonraki adım, varsayılan olarak true olan değerini doğrulayarak çözümlenen örneğin önbelleğe alınması gerekip gerekmediğini kontrol etmektir. Son olarak, örneği uygulama kapsayıcısından alıyoruz, bizim durumumuzda bu, dizesine bağlı herhangi bir sınıfı sağlamasını istemek gibidir. $app $cached static::$app['router'] router Şimdi, uygulama kabının bir örneği, yani bir olmasına rağmen neden bir dizi gibi eriştiğimizi merak edebilirsiniz. Haklısın! Ancak uygulama kapsayıcısı, dizi benzeri erişime izin veren adlı bir PHP arabirimi uygular. Bu gerçeği teyit etmek için şuna bir göz atabiliriz: $app nesne ArrayAccess <?php namespace Illuminate\Container; use ArrayAccess; // <- this guy use Illuminate\Contracts\Container\Container as ContainerContract; class Container implements ArrayAccess, ContainerContract { // ... } Dolayısıyla, gerçekten de dizesine bağlı bir örneği, özellikle de döndürür. Nasıl bildim? cephesine bir göz atın; Çoğu zaman, bu cephenin neyi gizlediğine veya daha kesin olarak yöntem çağrılarımızın hangi sınıfa proxy uygulanacağına dair ipucu veren bir PHPDoc bulacaksınız. resolveFacadeInstance() router \Illuminate\Routing\Router Route @see Şimdi yöntemimize geri dönelim. __callStatic <?php namespace Illuminate\Support\Facades; use RuntimeException; // ... abstract class Facade { // ... public static function __callStatic(string $method, array $args): mixed { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } return $instance->$method(...$args); } } sınıfının bir nesnesi olan sahibiz. Ayarlanıp ayarlanmadığını test ederiz (ki bu bizim durumumuzda onaylanmıştır) ve doğrudan üzerindeki yöntemi çağırırız. Böylece bitiriyoruz. \Illuminate\Routing\Router $instance // Facade.php return $instance->get('/', Closure()); Artık öğesinin sınıfı içinde mevcut olduğunu doğrulayabilirsiniz. get() \Illuminate\Routing\Router <?php namespace Illuminate\Routing; use Illuminate\Routing\Route; use Illuminate\Contracts\Routing\BindingRegistrar; use Illuminate\Contracts\Routing\Registrar as RegistrarContract; // ... class Router implements BindingRegistrar, RegistrarContract { // ... public function get(string $uri, array|string|callable|null $action = null): Route { return $this->addRoute(['GET', 'HEAD'], $uri, $action); } } Bu konuyu tamamlıyor! Sonuçta bu çok zor olmadı mı? Özetlemek gerekirse, bir cephe konteynere bağlı bir dize döndürür. Örneğin , sınıfına bağlı olabilir. Bir cephe üzerinde tanımsız bir metodu statik olarak çağırdığımızda örneğin devreye devreye giriyor. hello-world HelloWorld HelloWorldFacade __callStatic() yönteminde kayıtlı dizeyi, kapsayıcı içinde bağlı olan şeye çözer ve çağrımızı, alınan bu örneğe proxy olarak yönlendirir. Böylece ile son buluruz. İşin özü bu! Hala tıklamadınız mı? O zaman cephemizi oluşturalım! getFacadeAccessor() (new HelloWorld())->method() Cephemizi Yapalım Diyelim ki bu sınıfa sahibiz: <?php namespace App\Http\Controllers; class HelloWorld { public function greet(): string { return "Hello, World!"; } } Amaç çağırmaktır. Bunu yapmak için sınıfımızı uygulama konteynerine bağlayacağız. İlk önce gidin. HelloWorld::greet() AppServiceProvider <?php namespace App\Providers; use App\Http\Controllers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind('hello-world', function ($app) { return new HelloWorld; }); } // ... } Artık uygulama kapsayıcımızdan (veya daha önce bahsettiğim gibi kutudan) isteğinde bulunduğumuzda, örneğini döndürür. Ne kaldı? Basitçe dizesini döndüren bir cephe oluşturun. hello-world HelloWorld hello-world <?php namespace App\Http\Facades; use Illuminate\Support\Facades\Facade; class HelloWorldFacade extends Facade { protected static function getFacadeAccessor() { return 'hello-world'; } } Bunu yerine getirdiğimizde, onu kullanmaya hazırız. Bunu web.php. <?php use App\Http\Facades; use Illuminate\Support\Facades\Route; Route::get('/', function () { return HelloWorldFacade::greet(); // Hello, World! }); cephesinde öğesinin bulunmadığını biliyoruz, tetikleniyor. Uygulama kapsayıcısından bir dizeyle (bizim durumumuzda ) temsil edilen bir sınıfı çeker. Ve bu bağlamayı zaten yaptık; Birisi bir isteğinde bulunduğunda ona bir örneği sağlaması talimatını verdik. Sonuç olarak, gibi herhangi bir çağrı, alınan örneği üzerinde çalışacaktır. Ve bu kadar. HelloWorldFacade greet() __callStatic() hello-world AppServiceProvider hello-world HelloWorld greet() HelloWorld Tebrikler! Kendi cephenizi yarattınız! Laravel Gerçek Zamanlı Cepheler Artık cepheleri iyi anladığınıza göre, ortaya çıkarmanız gereken bir sihir numarası daha var. kullanarak bir cephe oluşturmadan işlevini arayabildiğinizi hayal edin. Gerçek zamanlı cepheleri HelloWorld::greet() Bir bakalım: <?php use Facades\App\Http\Controllers; // Notice the prefix use Illuminate\Support\Facades\Route; Route::get('/', function () { return HelloWorld::greet(); // Hello, World! }); Denetleyicinin ad alanının önüne ekleyerek, daha önce elde ettiğimiz sonucun aynısını elde ederiz. Ancak, denetleyicisinin adında herhangi bir statik yönteme sahip olmadığı kesindir! Peki nereden geliyor? Bunun bir tür büyücülük gibi görünebileceğinin farkındayım ama bir kere anladığınızda oldukça basit. Facades HelloWorld greet() Facades\App\Http\Controllers\HelloWorld Daha önce kontrol ettiğimiz \Illuminate\Foundation\Bootstrap\RegisterFacades $app: <?php namespace Illuminate\Foundation\Bootstrap; use Illuminate\Contracts\Foundation\Application; use Illuminate\Foundation\AliasLoader; use Illuminate\Foundation\PackageManifest; use Illuminate\Support\Facades\Facade; class RegisterFacades { public function bootstrap(Application $app): void { Facade::clearResolvedInstances(); Facade::setFacadeApplication($app); AliasLoader::getInstance(array_merge( $app->make('config')->get('app.aliases', []), $app->make(PackageManifest::class)->aliases() ))->register(); // Interested here } } En sonunda yönteminin çağrıldığını görebilirsiniz. İçeriye bir göz atalım: register() <?php namespace Illuminate\Foundation; class AliasLoader { // ... protected $registered = false; public function register(): void { if (! $this->registered) { $this->prependToLoaderStack(); $this->registered = true; } } } değişkeni başlangıçta olarak ayarlanmıştır. Bu nedenle ifadesini girip yöntemini çağırıyoruz. Şimdi uygulamasını inceleyelim. $registered false if prependToLoaderStack() // AliasLoader.php protected function prependToLoaderStack(): void { spl_autoload_register([$this, 'load'], true, true); } Sihir yapılan yer burasıdır! Laravel, tanımsız bir sınıfa erişmeye çalışıldığında tetiklenen yerleşik bir PHP işlevi olan işlevini çağırıyor. Bu gibi durumlarda yürütülecek mantığı tanımlar. Bu durumda Laravel, tanımsız bir çağrıyla karşılaştığında yöntemini çağırmayı seçer. spl_autoload_register() load() Ek olarak , tanımlanmamış sınıfın adını çağırdığı yönteme veya işleve otomatik olarak iletir. spl_autoload_register() yöntemini inceleyelim; anahtar olmalı. load() // AliasLoader.php public function load($alias) { if (static::$facadeNamespace && str_starts_with($alias, static::$facadeNamespace)) { $this->loadFacade($alias); return true; } if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } } ayarlanıp ayarlanmadığını kontrol ederiz ve bizim durumumuzda hangi sınıf geçtiyse, ayarlananla başlar $facadeNamespace Facades\App\Http\Controllers\HelloWorld $facadeNamespace Mantık, ayarlanıp ayarlanmadığını ve aktarılan sınıfın (bizim durumumuzda (tanımsız) olup olmadığını) $facadeNamespace Facades\App\Http\Controllers\HelloWorld $facadeNamespace. // AliasLoader.php protected static $facadeNamespace = 'Facades\\'; Koşulları karşılayarak denetleyicimizin ad alanının önüne eklediğimiz için, işlemine geçiyoruz Facades loadFacade() // AliasLoader.php protected function loadFacade($alias) { require $this->ensureFacadeExists($alias); } Burada yöntem, öğesinden döndürülen yolu gerektirir. Dolayısıyla bir sonraki adım, bunun uygulanmasına girmektir. ensureFacadeExists() // AliasLoader.php protected function ensureFacadeExists($alias) { if (is_file($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) { return $path; } file_put_contents($path, $this->formatFacadeStub( $alias, file_get_contents(__DIR__.'/stubs/facade.stub') )); return $path; } Öncelikle isimli bir dosyanın var olup olmadığı kontrol ediliyor. Bizim durumumuzda bu dosya mevcut değil ve bir sonraki adımı tetikliyor: . Bu işlev bir dosya oluşturur ve onu belirtilen kaydeder. Dosyanın içeriği, adına bakılırsa bir saplamadan bir cephe oluşturan tarafından oluşturulur. Eğer incelerseniz aşağıdakileri bulursunuz: framework/cache/facade-'.sha1($alias).'.php' file_put_contents() $path formatFacadeStub() facade.stub <?php namespace DummyNamespace; use Illuminate\Support\Facades\Facade; /** * @see \DummyTarget */ class DummyClass extends Facade { /** * Get the registered name of the component. */ protected static function getFacadeAccessor(): string { return 'DummyTarget'; } } Tanıdık geliyor? Aslında bunu manuel olarak yaptık. Artık , önekini kaldırdıktan sonra boş içeriği tanımsız sınıfımızla değiştiriyor. Bu güncellenmiş cephe daha sonra saklanır. Sonuç olarak, bir dosya gerektirdiğinde bunu doğru bir şekilde yapar ve sonunda aşağıdaki dosyayı gerektirir: formatFacadeStub() Facades\\ loadFacade() <?php namespace Facades\App\Http\Controllers; use Illuminate\Support\Facades\Facade; /** * @see \App\Http\Controllers\HelloWorld */ class HelloWorld extends Facade { /** * Get the registered name of the component. */ protected static function getFacadeAccessor(): string { return 'App\Http\Controllers\HelloWorld'; } } Ve şimdi, olağan akışta, uygulama kapsayıcısından dizesine bağlı herhangi bir örneği döndürmesini istiyoruz. Bu dizeyi hiçbir şeye bağlamadığımızı, bile dokunmadığımızı merak ediyor olabilirsiniz. Ama en başta uygulama kabı hakkında bahsettiğimi hatırlıyor musunuz? App\Http\Controllers\HelloWorld AppServiceProvider , ancak bir şartla sınıfın bir yapıcısının olmaması gerekir. Aksi halde onu sizin için nasıl inşa edeceğini bilemezdi. Bizim durumumuzda sınıfımızın oluşturulması için herhangi bir argümana ihtiyacı yoktur. Böylece kapsayıcı bunu çözer, geri gönderir ve tüm çağrılar ona proxy olarak aktarılır. Kutu boş olsa bile örneği döndürür HelloWorld Gerçek zamanlı cephelerin özetlenmesi: Sınıfımıza ön ekini ekledik. Uygulama önyüklemesi sırasında Laravel, tanımsız sınıfları çağırdığımızda tetiklenen dosyasını kaydeder. Sonunda yöntemine yol açar. içinde, mevcut tanımsız sınıfın önekiyle eklenip eklenmediğini kontrol ederiz. Eşleşiyor, dolayısıyla Laravel onu yüklemeye çalışıyor. Facades spl_autoload_register() load() load() Facades Cephe mevcut olmadığından onu bir saplamadan oluşturur ve ardından dosyayı gerektirir. Ve işte! Sıradan bir cepheniz var ama bu anında yaratıldı. Oldukça hoş, değil mi? Çözüm Bu noktaya kadar geldiğiniz için tebrikler! Bunun biraz bunaltıcı olabileceğini anlıyorum. Geri dönmekten ve sizin için tam olarak tatmin edici olmayan bölümleri yeniden okumaktan çekinmeyin. IDE'nizi takip etmek de yardımcı olabilir. Ama hey, artık kara büyü yok, iyi hissettiriyor olmalı, en azından ilk seferinde böyle hissettim! Ve unutmayın, bir dahaki sefere bir yöntemi statik olarak çağırdığınızda durum böyle olmayabilir 🪄