• Czas czytania ~4 min
  • 12.02.2024

Przywitaj się z atrybutami PHP Atrybuty oferują możliwość dodawania ustrukturyzowanych, czytelnych maszynowo informacji metadanych do deklaracji w kodzie: Klasy, metody, funkcje, parametry, właściwości i stałe klas mogą być celem atrybutu

.

Uważam, że definicja jest trafna i jestem pewien, że większość programistów czytających ten artykuł przynajmniej raz spotkała się z atrybutami. Jeśli tego nie zrobiłeś, są to zasadniczo metadane dodane do klasy.

W tym momencie możesz się zastanawiać, czym różnią się od PHPDOC-ów ? Cóż, są to pierwszorzędni obywatele, są prawdziwymi klasami PHP i tak, wiem, to zmienia teraz całą grę; nie musisz pisać wyrażeń regularnych , aby wyodrębnić rzeczy z PHPDocs, a nawet możesz zachować jakąś formę stanu we właściwościach.

Ponieważ jestem trochę spóźniony na imprezę, klasycznych przykładów atrybutów jest mnóstwo. Dlaczego więc nie zbudować z nich czegoś fajnego?

Przełączanie

tras Podczas pracy z zespołem często otrzymuję wiadomości od innych programistów (frontendowców, patrzę na was), powiadamiających mnie, że trasa nie działa zgodnie z oczekiwaniami. Czasami żałuję, że nie mogę łatwo wyłączyć trasy dla określonego środowiska, takiego jak środowisko przejściowe, zachowując jednocześnie jego funkcjonalność lokalnie. W ten sposób ja i moi koledzy programiści backendowi możemy nad nim pracować, wypychać kod i utrzymywać nasz typowy przepływ pracy bez obaw o przypadkowe użycie. Czasami jest to po prostu nowa trasa, która musi pozostać dostępna wyłącznie w środowisku testowym.

Zastanawiając się nad tym, pomyślałem, że fajnie by było, gdybym mógł oznaczyć akcję jako wyłączoną lub zignorowaną. I wiecie co? Dzięki atrybutom okazało się to bardzo łatwe, a także super czyste.

Zacznijmy od stworzenia atrybutu. Nazwę mój Ignorei będzie miał jedną właściwość o nazwie in

<?php
namespace App\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class Ignore
{
    public function __construct(
        public array $in = ['production']
    ) {
    }
}

To wszystko, właśnie utworzyłeś atrybut, zauważysz również, że ograniczyliśmy jego zakres do klas i metod, pozwalając na umieszczenie tego atrybutu wyłącznie na tych dwóch encjach.

Teraz możemy go użyć w ten sposób, że widać, że

namespace App\Http\Controllers;
use App\Attributes\Ignore;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Symfony\Component\HttpFoundation\Response
class TwoFactorQrCodeController extends Controller
{
    #[Ignore(in: ['production', 'staging'])]
    public function show(Request $request): Response
    {
        if (is_null($request->user()->two_factor_secret)) {
            return [];
        }
        return response()->json([
            'svg' => $request->user()->twoFactorQrCodeSvg(),
            'url' => $request->user()->twoFactorQrCodeUrl(),
        ]);
    }
}

to już dobrze się czyta, zignoruj w produkcji i stagingu. Mimo to musimy sprawić, by było to funkcjonalne i istnieje kilka metod, aby to osiągnąć, najprostszą jest użycie oprogramowania pośredniczącego.

Stwórzmy oprogramowanie pośredniczące, nazwę je IsRouteIgnored, możesz wybrać dowolną nazwę

php artisan make:middleware IsRouteIgnored

Teraz możemy zaimplementować logikę, idea jest prosta: przechwytujemy żądania tras, które używają tego oprogramowania pośredniczącego, następnie sprawdzamy, czy akcja ma atrybut, Ignore jeśli tak, sprawdzamy, czy bieżące środowisko może mieć tę trasę, czy nie.

W tym celu wykorzystamy magię Reflection API, zanurzmy się w kodzie

<?php
namespace App\Http\Middleware;
use Closure;
use ReflectionMethod;
use App\Attributes\Ignore;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Symfony\Component\HttpFoundation\Response;
class IsRouteIgnored
{
    public function handle(Request $request, Closure $next): Response
    {
        $route = $request->route();
        if (!($route instanceof Route) || $route->action['uses'] instanceof Closure) {
            return $next($request);
        }
        $reflection = new ReflectionMethod($route->getControllerClass(), $route->getActionMethod());
        $attributes = $reflection->getAttributes(Ignore::class);
        if (!empty($attributes) && in_array(config('app.env'), $attributes[0]->newInstance()->in)) {
            abort(404);
        }
        return $next($request);
    }
}

Tworzymy odbicie metody, do której prowadzi trasa, więc pobieramy atrybut. Ignore Domyślnie atrybuty nie są powtarzalne, co oznacza, że można ich użyć tylko raz na jednostkę. Ponieważ określiliśmy nasze zainteresowanie wyłącznie atrybutem Ignore , otrzymamy tablicę jednoelementową.

Możemy teraz utworzyć instancję atrybutu, wywołując newInstance(), powracając do sfery zwykłych klas. Następnie możemy sprawdzić środowiska, w których ta trasa powinna być ignorowana w in obrębie właściwości. W takim przypadku trasa 404 zwróci odpowiedź dla środowisk produkcyjnych i przejściowych, ale będzie działać w środowiskach lokalnych i testowych.

Następnie możesz zarejestrować oprogramowanie pośredniczące globalnie lub w trasach interfejsu API, tak jak zwykle, i możesz zacząć ignorować trasy, oznaczając je atrybutem.

Wnioski

Za pomocą zaledwie kilku linijek kodu włączyliśmy przełączane trasy. Chociaż implementacja była stosunkowo prosta, przykład miał na celu pokazanie potęgi atrybutów. To znaczy, daj spokój, jakie to fajne? Włączając i wyłączając trasy w określonych wybranych środowiskach, możesz nawet dostosować atrybut, aby wykluczyć trasę ze wszystkich środowisk z wyjątkiem tych, które określisz, Ignore a opcje są nieograniczone.

Następnym razem, gdy będziecie się zastanawiać nad oznaczeniem danej klasy jako czegoś konkretnego, zastanówcie się nad wypróbowaniem atrybutów! 🪄

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297