Как стать автором
Обновить

Глубокое погружение в PSR стандарты PHP

Уровень сложностиСредний

Введение в PSR стандарты

PSR (PHP Standards Recommendations) - это набор стандартов, разработанных PHP-FIG (PHP Framework Interoperability Group) для обеспечения совместимости между различными PHP-компонентами и фреймворками.

Основные PSR стандарты:

  1. PSR-1: Basic Coding Standard

  2. PSR-2: Coding Style Guide (устарел, заменен на PSR-12)

  3. PSR-3: Logger Interface

  4. PSR-4: Autoloading Standard

  5. PSR-6: Caching Interface

  6. PSR-7: HTTP Message Interface

  7. PSR-11: Container Interface

  8. PSR-12: Extended Coding Style Guide

  9. PSR-15: HTTP Handlers

  10. PSR-16: Simple Cache

Детальный разбор ключевых стандартов

PSR-1: Basic Coding Standard

Основные требования:

  • Файлы должны использовать только <?php или <?= теги

  • Файлы должны быть в UTF-8 без BOM

  • Файлы должны либо объявлять символы (классы, функции, константы), либо производить побочные эффекты (вывод, изменение ini и т.д.), но не то и другое вместе

  • Имена классов в стиле StudlyCaps

  • Константы класса в верхнем регистре с подчеркиваниями

  • Методы в стиле camelCase

Пример плохого кода:

<?ph
// Побочный эффект: изменение настроек
ini_set('display_errors', 0);

// Объявление
class bad_example {
    const badCONSTANT = 'value';
    
    public function badmethod() {
        // ...
    }
}

Хороший пример по PSR-1:

<?php
// Только объявления
class GoodExample
{
    const GOOD_CONSTANT = 'value';
    
    public function goodMethod()
    {
        // ...
    }
}

// Другой файл:
<?php
// Только побочные эффекты
ini_set('display_errors', 0);
echo "Hello World";

PSR-12: Extended Coding Style Guide

Расширенное руководство по стилю кода, заменяет PSR-2.

Основные правила:

  • Использование 4 пробелов для отступов

  • Открывающая фигурная скобка класса на новой строке

  • Открывающая фигурная скобка метода на новой строке

  • Видимость должна быть объявлена для всех свойств и методов

  • Ключевые слова в нижнем регистре (true, false, null)

  • Строгий тип (strict_types=1) рекомендуется

Пример класса по PSR-12:

<?php

declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\SomeNamespace\ClassA;
use Vendor\Package\SomeNamespace\ClassB;
use Vendor\Package\SomeNamespace\ClassC as C;

class Example
{
    private const SOME_CONST = 'constant';

    private $property;

    public function __construct($property)
    {
        $this->property = $property;
    }

    public function someMethod(): string
    {
        if ($this->property === null) {
            return 'null';
        }

        if ($this->property === 'value') {
            return 'value';
        }

        return $this->property;
    }
}

PSR-4: Autoloader Standard

Стандарт автозагрузки классов. Основные концепции:

  • Полное имя класса имеет вид: \<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>

  • Пространства имен соответствуют структуре директорий

  • Имя класса должно совпадать с именем файла

Пример структуры проекта:

project/
    src/
        Acme/
            Blog/
                Post.php
                Comment.php
    vendor/
    ...

Post.php:

<?php
namespace Acme\Blog;

class Post
{
    // ...
}

composer.json:

{
    "autoload": {
        "psr-4": {
            "Acme\\Blog\\": "src/Acme/Blog"
        }
    }
}

PSR-3: Logger Interface

Стандартизированный интерфейс для логгеров.

Пример использования:

<?php
use Psr\Log\LoggerInterface;

class Application
{
    private $logger;
    
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
    
    public function run()
    {
        try {
            // код приложения
            $this->logger->info('Application started');
        } catch (\Exception $e) {
            $this->logger->error('Error occurred', ['exception' => $e]);
        }
    }
}

PSR-7: HTTP Message Interface

Стандартизированные интерфейсы для HTTP-сообщений (запросов и ответов).

Пример middleware:

<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AuthenticationMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request, 
        RequestHandlerInterface $handler
    ): ResponseInterface {
        if (!$this->isAuthenticated($request)) {
            return new Response(401, [], 'Unauthorized');
        }
        
        return $handler->handle($request);
    }
    
    private function isAuthenticated(ServerRequestInterface $request): bool
    {
        // Проверка аутентификации
    }
}

Лучшие практики

1. Использование strict_types

<?php
declare(strict_types=1);

function add(int $a, int $b): int
{
    return $a + $b;
}

// Будет ошибка TypeError, если передать не integer
add(1, 2); // OK
add("1", "2"); // TypeError

2. Правильное использование пространств имен

<?php
namespace Acme\Blog\Domain\Model;

use Acme\Blog\Domain\Repository\PostRepository;
use Acme\Blog\Domain\Exception\InvalidTitleException;

class Post
{
    private $title;
    private $content;
    
    public function __construct(string $title, string $content)
    {
        if (empty($title)) {
            throw new InvalidTitleException('Title cannot be empty');
        }
        
        $this->title = $title;
        $this->content = $content;
    }
    
    // ...
}

3. Использование интерфейсов PSR

<?php
namespace Acme\Blog\Infrastructure\Persistence;

use Psr\Log\LoggerInterface;
use Acme\Blog\Domain\Repository\PostRepository;
use Acme\Blog\Domain\Model\Post;

class DoctrinePostRepository implements PostRepository
{
    private $logger;
    
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
    
    public function save(Post $post): void
    {
        try {
            // Сохранение поста
            $this->logger->info('Post saved', ['id' => $post->getId()]);
        } catch (\Exception $e) {
            $this->logger->error('Error saving post', ['exception' => $e]);
            throw $e;
        }
    }
}

4. Middleware подход с PSR-15

<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class CorsMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        $response = $handler->handle($request);
        
        return $response
            ->withHeader('Access-Control-Allow-Origin', '*')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    }
}

5. Кэширование с PSR-6

<?php
use Psr\Cache\CacheItemPoolInterface;

class ExpensiveCalculationService
{
    private $cache;
    
    public function __construct(CacheItemPoolInterface $cache)
    {
        $this->cache = $cache;
    }
    
    public function calculate(string $key): float
    {
        $item = $this->cache->getItem($key);
        
        if ($item->isHit()) {
            return $item->get();
        }
        
        $result = $this->doExpensiveCalculation();
        
        $item->set($result);
        $item->expiresAfter(3600); // 1 час
        $this->cache->save($item);
        
        return $result;
    }
    
    private function doExpensiveCalculation(): float
    {
        // Тяжелые вычисления
        usleep(500000); // Имитация долгого расчета
        return 42.0;
    }
}

Заключение

Следование PSR стандартам и лучшим практикам PHP позволяет:

  • Обеспечить совместимость между разными компонентами

  • Улучшить читаемость и поддерживаемость кода

  • Упростить командную разработку

  • Создавать более надежное и предсказуемое ПО

Внедрение этих стандартов особенно важно в крупных проектах и при разработке компонентов для повторного использования.

Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.