Files
interview-demo-code/.cursor/rules/php.md
Nikita Kiselev f329bfa9d9
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
Squashed commit message
2026-03-11 23:00:23 +03:00

4.0 KiB
Raw Blame History

PHP Code Style Rules

PHP Version

The project supports PHP 7.4+

PSR Standards

  • PSR-1: Basic Coding Standard
  • PSR-4: Autoloading Standard
  • PSR-12: Extended Coding Style

Code Style

Type Declarations

// ✅ Correct  strict typing
public function getCustomers(Request $request): JsonResponse
{
    $id = (int) $request->get('id');
    return new JsonResponse(['data' => $customers]);
}

// ❌ Incorrect  no types
public function getCustomers($request)
{
    return ['data' => $customers];
}

Nullable Types

// ✅ Correct
public function findById(?int $id): ?array
{
    if ($id === null) {
        return null;
    }
    return $this->query->where('id', '=', $id)->firstOrNull();
}

Strict Types

Always use declare(strict_types=1);:

<?php

declare(strict_types=1);

namespace App\Services;

Array Syntax

// ✅ Preferred  short syntax
$array = ['key' => 'value'];

// ❌ Do not use
$array = array('key' => 'value');

String Interpolation

// ✅ Preferred
$message = "User {$userId} not found";

// ✅ Alternative
$message = sprintf('User %d not found', $userId);

Arrow Functions (PHP 7.4+)

// ✅ For simple operations
$filtered = array_filter($items, fn($item) => $item->isActive());

// ❌ For complex logic  use regular functions

Nullsafe Operator (PHP 8.0+)

// ✅ For PHP 7.4
$name = $user && $user->profile ? $user->profile->name : null;

Naming Conventions

Classes

// ✅ PascalCase
class TelegramCustomerService {}
class UserRepository {}

Methods

// ✅ camelCase
public function getCustomers(): array {}
public function saveOrUpdate(array $data): array {}

Variables

// ✅ camelCase
$customerData = [];
$totalRecords = 0;

Constants

// ✅ UPPER_SNAKE_CASE
private const MAX_RETRIES = 3;
public const DEFAULT_PAGE_SIZE = 20;

Private Properties

// ✅ camelCase with visibility modifier
private string $tableName;
private Builder $builder;

Documentation

PHPDoc

/**
 * @throws ValidationException If parameters are invalid
 */
public function getCustomers(Request $request): JsonResponse
{
    // ...
}

Inline Comments

// ✅ Useful comments
// Apply filters to calculate total number of records
$countQuery = $this->buildCountQuery($filters);

// ❌ Obvious comments
// Get data
$data = $this->getData();

Error Handling

Exceptions

// ✅ Specific exceptions
if (!$userId) {
    throw new InvalidArgumentException('User ID is required');
}

// ✅ Logging
try {
    $result = $this->service->process();
} catch (Exception $e) {
    $this->logger->error('Processing failed', [
        'exception' => $e,
        'context' => $context,
    ]);
    throw new ProcessingException('Failed to process', 0, $e);
}

Query Builder Usage

Always Use Query Builder

// ✅ Correct
$customers = $this->builder->newQuery()
    ->select(['id', 'name', 'email'])
    ->from('acmeshop_customers')
    ->where('status', '=', 'active')
    ->orderBy('created_at', 'DESC')
    ->get();

// In edge cases raw SQL may be used
$result = $this->database->query("SELECT * FROM acmeshop_customers");

Parameter Binding

// ✅ Query Builder automatically binds parameters
$query->where('name', 'LIKE', "%{$search}%");

// ❌ Never concatenate values into SQL, avoid SQL Injection.

Array Access

Safe Array Access

// ✅ Use Arr::get()
use Acme\ECommerceFramework\Support\Arr;

$value = Arr::get($data, 'key', 'default');

// ❌ Unsafe
$value = $data['key']; // may trigger an error

Return Types

// ✅ Always specify return type
public function getData(): array {}
public function findById(int $id): ?array {}
public function process(): void {}

// ❌ Without type
public function getData() {}

Visibility Modifiers

// ✅ Always specify visibility modifier
private string $tableName;
protected Builder $builder;
public function getData(): array {}