1 203
Подписчики
+224 часа
-17 дней
+7930 день
Архив постов
1 203
Стрімимо завтра о 15:00.
Почнемо будувати мобільну апку для Комуналки з самого початку. Чекатиму всіх!
https://youtube.com/live/o2j68J2JONU
1 203
Тут ще й YouTube подгончик накинув. Тепер можу вам на коментарі голосом відповідати.
Ну кайф же ж.
Доречі, завтра стрімимо?
1 203
А тим часом мою бібліотечку по роботі з платіжкою WayForPay для C# та .NET прям качають. Приємно.
Значить вчився і тренувався не дарма.
А що зараз вчите ви? Які нові навички в собі тренуєте? 😉
1 203
Вінду не люблю, але тут майки молодці.
Кому треба сенйор, щоб вчитися? Пишіть в особисті! 😎
1 203
Ви мабуть вже зрозуміли, що я дуже люблю творчість Митця Леся Подервʼянського. Так от чувак написав ІМБІЩУ для Claude Code. Дуже раджу!
https://dev.ua/news/mytets-plugin-1771839543
1 203
Тут якраз корисні тіпси як обмеження в 5 годин обійти запостили на DOU. Користуйтесь!
https://dou.ua/forums/topic/58050/?from=tg
1 203
Repost from N/a
#випитали
Отже, що таке PHP атрибути?
Атрибути — це метадані, які ти можеш навішувати на класи, методи, властивості та аргументи.
По суті — "анотації нового покоління" (як у Java або C#), але повністю типізовані.
PHP вже обросло багатьма корисними нативними атрибутами. До прикладу, чи не щодня я користуюсь
#[Deprecated].
Але, як створити кастомний атрибут?
Давайте розберемо на простому прикладі. Нам потрібен атрибут, який буде перевіряти роль юзера (через пакет від Spatie чи кастомну логіку. Тут неважливо).
Створюєш клас-атрибут.
Він обов’язково має бути позначений #[Attribute].
<?php
namespace App\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
class RequiresRole
{
public function __construct(
public string $role
) {}
}
#[Attribute] робить цей клас атрибутом.
`TARGET_METHOD`― наш атрибут можна вішати лише на методи.
В конструктор передаємо дані (role), і вони доступні в Reflection.
Можеш також вказати:
Attribute::TARGET_CLASS
Attribute::TARGET_PROPERTY
Attribute::TARGET_PARAMETER
Attribute::TARGET_FUNCTION
Attribute::IS_REPEATABLE
Використання атрибуту.
Припустимо, у контролері:
use App\Attributes\RequiresRole;
class UserController
{
#[RequiresRole('admin')]
public function destroy(int $id): void
{
// ...
}
}
Або загадаємо минулу тему. Зчитування атрибуту через Reflection.
Десь у middleware або сервісі авторизації:
$controller = app(UserController::class);
$method = new \ReflectionMethod($controller, 'destroy');
$attributes = $method->getAttributes(RequiresRole::class);
if (!empty($attributes)) {
$instance = $attributes[0]->newInstance();
$requiredRole = $instance->role;
// Далі робиш свою логіку
if (auth()->user()->role !== $requiredRole) {
abort(403);
}
}
$instance->role — це значення, яке ми передали у #[RequiresRole('admin')].
Куди це інтегрується в Laravel?
- Middleware (авторизація)
Підтягує controlled method → читає атрибути → вирішує, пускати чи ні.
- Кастомні роутери
Можна зробити систему маршрутизації через атрибути замість Route::get().
- Генерація документації
Типу OpenAPI через атрибути на методах.
- Валідація
Можна робити:
#[MinLength(10)]
public string $name;
Маленький лайфхак.
Хочеш, щоб атрибут можна було повторювати?
Наприклад:
#[Tag('admin')]
#[Tag('billing')]
Пиши так:
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
class Tag
{
public function __construct(public string $name) {}
}1 203
Repost from N/a
<?php
namespace App\Http\Controllers;
use App\Attributes\Permission;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class UserController extends Controller
{
#[Permission('users.delete')]
public function destroy(User $user): RedirectResponse
{
// Якщо ми сюди дійшли — значить permission пройшов перевірку
$user->delete();
return redirect()
->route('users.index')
->with('status', 'User deleted');
}
#[Permission('users.view')]
public function show(User $user): View
{
return view('users.show', compact('user'));
}
}
Тут логіка проста:
- На метод destroy навішено #[Permission('users.delete')]
- Middleware це прочитає й перевірить.
- Якщо користувач не має permission users.delete — 403.
Підключаємо middleware до route.
У routes/web.php:
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth', 'permission.attribute'])
->group(function () {
Route::delete('/users/{user}', [UserController::class, 'destroy'])
->name('users.destroy');
Route::get('/users/{user}', [UserController::class, 'show'])
->name('users.show');
});
Тут важливий момент:
Middleware викликається, а вже всередині він дивиться, чи є атрибут у поточного методу.
Мінімальний метод hasPermission для юзера в моделі.
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
// ...
public function hasPermission(string $permission): bool
{
// Дуже примітивно: припустимо, у нас є json-поле permissions
// в БД і там масив прав типу ['users.view', 'users.delete']
$permissions = $this->permissions ?? [];
if (is_string($permissions)) {
$permissions = json_decode($permissions, true) ?: [];
}
return in_array($permission, $permissions, true);
}
}
У реальному житті ти, звісно, скоріше за все:
- або прикрутиш Spatie Permissions,
- або політики (policy),
- або ролі/групи.
Що ти з цього маєш зрозуміти:
1. Атрибут — просто клас з #[Attribute], який вішається над методом.
2. Reflection — дає змогу прочитати цей атрибут під час виконання.
3. Middleware — хороше місце, щоб інтерпретувати атрибути контролера перед тим, як виконати метод.
4. Логіка вийшла:
роут → middleware permission.attribute → дивиться атрибути на методі → перевіряє права → або 403, або пускає.1 203
Repost from N/a
А тепер розберемо більш практичні приклади.
Зробимо реальний робочий приклад під Laravel: атрибут
Permission, Middleware, реєстрація і використання в контролері.
Буду йти крок за кроком, щоб ти міг це повторити в реальному проєкті.
1. Створюємо атрибут Permission
<?php
namespace App\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
class Permission
{
public function __construct(
public string $name
) {}
}
Що тут відбувається:
#[Attribute(...)] — позначає, що це PHP-атрибут.
TARGET_METHOD — цей атрибут можна вішати тільки на методи (наприклад, методи контролера).
public string $name `— сюди ми передаємо назву права доступу, типу: 'users.delete'`.
2. Створюємо middleware, який читає атрибут
<?php
namespace App\Http\Middleware;
use App\Attributes\Permission;
use Closure;
use Illuminate\Http\Request;
use ReflectionMethod;
use Symfony\Component\HttpFoundation\Response;
class CheckPermissionAttribute
{
public function handle(Request $request, Closure $next): Response
{
$route = $request->route();
// Якщо раптом route без контролера (closure і т.п.)
if (! $route) {
return $next($request);
}
$controller = $route->getController();
$methodName = $route->getActionMethod();
// Якщо це не контролерний метод — не чіпаємо
if (! $controller || ! $methodName) {
return $next($request);
}
$reflectionMethod = new ReflectionMethod($controller, $methodName);
// Дістаємо всі атрибути типу Permission
$attributes = $reflectionMethod->getAttributes(Permission::class);
// Якщо атрибутів нема — просто пропускаємо далі
if (empty($attributes)) {
return $next($request);
}
/** @var Permission $permissionAttributeInstance */
$permissionAttributeInstance = $attributes[0]->newInstance();
$permissionName = $permissionAttributeInstance->name;
// Тут вже твоя логіка перевірки прав
// Можна через Spatie Permission, через policy,
// або просто поле role/permissions у юзера
$user = $request->user();
if (! $user) {
abort(401); // неавторизований
}
// Варіант 1: якби був Spatie:
// if (! $user->can($permissionName)) { ... }
// Для простоти: припустимо в юзера є метод hasPermission(string $name)
if (! method_exists($user, 'hasPermission') || ! $user->hasPermission($permissionName)) {
abort(403, 'Forbidden');
}
return $next($request);
}
}
Ключове тут:
- Дістаємо контролер та метод з $request->route().
- Через ReflectionMethod читаємо атрибут Permission.
- Якщо атрибут є — витягуємо назву права і перевіряємо користувача.
- Якщо користувач не має права → abort(403).
Реєструємо middleware в bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
// ...
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'permission.attribute' => \App\Http\Middleware\CheckPermissionAttribute::class,
]);
})
->create();
Тепер у роутів є middleware з назвою permission.attribute.
Використання в контролері.1 203
Repost from N/a
5. Автоматична генерація документації / OpenAPI
Багато бібліотек (Swagger Generator, Blueprint) використовують Reflection, щоб:
- читати типи параметрів
- генерувати схеми моделей
- документувати ендпоінти згідно з атрибутами
Тобто документування на основі коду — це повністю Reflection.
6. Validation Rules: автоматичний аналіз моделей
Уяви випадок: ти хочеш створити форму CRUD, і на основі моделі автоматично будувати rules.
$model = new User();
$reflection = new ReflectionClass($model);
foreach ($reflection->getProperties() as $property) {
// Можна аналізувати типи, атрибути і генерувати правила
}
Такі штуки роблять адмін-панелі типу Laravel Nova або Fillament.
7. Тестування — мокінг приватних методів (коли дуже треба)
По-перше, якщо тобі треба тестувати приватні методи, то задай собі питання "що зі мною не так?".
По-друге, якщо ну ОТ ПРЯМ ДУУУУУЖЕ треба, то:
Reflection дозволяє отримати доступ навіть до приватних методів:
$method = new ReflectionMethod(MyService::class, 'calculateInternal');
$method->setAccessible(true);
$result = $method->invoke(new MyService(), 10);
Так — це милиця, але інколи потрібно для складної логіки.
Підсумую просто:
Reflection реально потрібна коли ти хочеш:
- автоматично реєструвати класи (Route, Command, Providers)
- читати атрибути (PHP Attributes)
- реалізувати власний DI-container або розширити Laravel-івський
- будувати код, базуючись на структурі інших класів
- генерувати документацію
- створювати фреймворк-подібні компоненти
Коли НЕ треба використовувати Reflection?
Коли ти можеш просто написати код руками — пиши руками. Reflection повільніший і ускладнює дебаг.
Його місце — у фреймворках, інструментах, генераторах, а не у кожному другому контролері.