Pull to refresh

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

Level of difficultyMedium

Введение в 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 позволяет:

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

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

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

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

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

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.