• Час читання ~1 хв
  • 12.02.2024

Привітайтеся з атрибутами PHP Атрибути пропонують можливість додавати структуровану, машинозчитувану інформацію метаданих про оголошення в коді: Класи, методи, функції, параметри, властивості та константи класу можуть бути ціллю атрибута

.

Я вважаю, що визначення правильне, і я впевнений, що більшість розробників, які читають цю статтю, хоча б раз стикалися з атрибутами. Якщо ви цього не зробили, то вони, по суті, є метаданими, доданими до класу.

На цьому етапі вам може бути цікаво, чим вони відрізняються від PHPDOC? Ну, вони першокласні громадяни, це справжні класи PHP, і так, я знаю, це змінює всю гру зараз; вам не потрібно писати регулярні вирази , щоб витягти речі з PHPDocs, і ви навіть можете підтримувати певну форму стану у властивостях.

Оскільки я трохи запізнився на вечірку, класичних прикладів атрибутів предостатньо. То чому б замість цього не побудувати з ними щось круте?

Перемикання

маршрутів Працюючи з командою, я часто отримую повідомлення від інших розробників (frontend-хлопців, я дивлюся на вас), які повідомляють про те, що маршрут працює не так, як очікувалося. Іноді мені хотілося б легко відключити маршрут для певного середовища, наприклад, проміжного середовища, зберігши його функціональність локально. Таким чином, я та мої колеги-бекенд-розробники можемо працювати над ним, надсилати код і підтримувати наш типовий робочий процес, не турбуючись про випадкове використання. Іноді це просто новий маршрут, який повинен залишатися ексклюзивним для середовища тестування.

Тож, розмірковуючи над цим, я подумав, що було б круто, якби я міг позначити дію як вимкнену або проігноровану. І знаєте що? З атрибутами це виявилося дуже просто, а також супер чисто.

Почнемо зі створення атрибута. Я назву свій Ignore, і він матиме єдину властивість під назвою in

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

Ось і все, ви щойно створили атрибут, ви також помітите, що ми обмежили його область видимості класами та методами, дозволивши розмістити цей атрибут виключно на цих двох сутностях.

Тепер ми можемо використовувати його так, щоб

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(),
        ]);
    }
}

Ви могли бачити, що це вже добре читається, ігнорувати у виробництві та постановці. Тим не менш, нам потрібно зробити це функціональним, і для цього є кілька методів, найпростішим є використання проміжного програмного забезпечення.

Давайте створимо проміжне програмне забезпечення, я назву його IsRouteIgnored, не соромтеся вибирати будь-яку назву, яка вам більше подобається

php artisan make:middleware IsRouteIgnored

Тепер ми можемо реалізувати логіку, ідея проста: ми перехоплюємо запити маршрутів, які використовують це проміжне програмне забезпечення, потім перевіряємо, чи має дія атрибут, якщо є, ми перевіряємо, Ignore чи дозволено поточному середовищу мати цей маршрут чи ні.

Для цього ми скористаємося магією Reflection API, давайте зануримося в код

<?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);
    }
}

Ми створюємо відображення методу, до якого веде маршрут, тому отримуємо Ignore атрибут. За замовчуванням атрибути не повторюються, тобто їх можна використовувати лише один раз для кожної сутності. Оскільки ми вказали свій інтерес виключно в атрибуті Ignore , ми отримаємо одноелементний масив.

Тепер ми можемо створити екземпляр атрибута, викликавши newInstance(), повертаючись до області звичайних класів. Потім ми можемо перевірити середовища, в яких цей маршрут повинен бути проігнорований у in властивості. У цьому випадку маршрут поверне 404 відповідь для виробничого та проміжного середовищ, але функціонуватиме у локальному та тестовому середовищах.

Після цього ви можете зареєструвати проміжне програмне забезпечення глобально або в межах маршрутів API, як ви зазвичай це робите, і ви можете почати ігнорувати маршрути, позначивши їх атрибутом.

Висновок

За допомогою лише кількох рядків коду ми ввімкнули маршрути, які можна перемикати. Хоча реалізація була відносно простою, приклад мав на меті продемонструвати силу атрибутів. Я маю на увазі, давай, як це круто? Вмикаючи та вимикаючи маршрути в певних середовищах на ваш вибір, ви навіть можете налаштувати атрибут, щоб виключити маршрут з усіх середовищ, окрім тих, які ви вказали, Ignore і варіанти нескінченні.

Наступного разу, коли ви будете розмірковувати над тим, щоб позначити клас як щось особливе, подумайте про те, щоб спробувати атрибути! 🪄

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

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...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297