🐘 PHP

Полный справочник: типы и строгая типизация, строки, массивы, функции, ООП, enum, исключения, PDO и безопасность, формы и сессии, Composer, тесты и экосистема.

Шпаргалки · Программирование · #php #web #backend #laravel

Запуск и окружение

php file.php                       php -S localhost:8000 -t public         php -v        php -m      # модули
composer init / require guzzlehttp/guzzle / install / update / dump-autoload
php artisan serve                  # Laravel;   bin/console  # Symfony

IDE: PhpStorm, VS Code (Intelephense). Версии: 8.2–8.4 актуальны. Стандарты: PSR-1/12 (стиль), PSR-4 (автозагрузка), PSR-7/15 (HTTP). Анализ и стиль: PHPStan, Psalm, PHP-CS-Fixer, Pint.

Основы

<?php
declare(strict_types=1);                  // строгая типизация скалярных типов

$name = 'Аня';                            // переменные начинаются с $
echo "Привет, $name! {$user['id']}\n";    // двойные кавычки подставляют переменные
define('MAX', 10); const MIN = 1;
var_dump($x); print_r($arr); dd($x);      // отладка (dd в Laravel)

Комментарии: //, #, /* */, документация /** */. Теги <?php ... ?>, в чистых PHP-файлах закрывающий тег не нужен.

Типы данных

Тип Пример
int, float 42, 3.14, 0x1A, 1_000_000
string 'a', "b $x", heredoc <<<EOT
bool, null true, null
array [1, 2], ['a' => 1]
object, callable, iterable, mixed, never, void
gettype($x); is_int($x); is_numeric('12.5'); (int)'12abc'; (string)5; intval('42'); floatval('1.5');
$x ?? 'по умолчанию'; $x ??= 5; $obj?->prop?->method();
empty('0'); isset($a['k']); array_key_exists('k', $a);

Ложные значения: false, 0, 0.0, '', '0', [], null. Сравнивайте ===, а не == ('abc' == 0 в старых версиях истинно; 0 == 'a' изменилось в PHP 8).

Операторы

+ - * / % **, . (склейка), == === != !== <=> < >, && || ! and or xor, ??, ?:, ?->, instanceof, ... (spread), => (в массивах и стрелочных функциях).

Строки

strlen($s); mb_strlen($s);                 // для UTF-8 используйте mb_*
str_contains($s, 'x'); str_starts_with($s, 'p'); str_ends_with($s, 's');
strpos($s, 'x'); substr($s, 1, 3); mb_substr($s, 0, 5);
strtoupper($s); mb_strtoupper($s); ucfirst($s); trim($s); str_replace('a', 'b', $s);
explode(',', $s); implode(', ', $arr); sprintf('%05.2f %s', 3.14159, 'x'); number_format(1234.5, 2, ',', ' ');
preg_match('/(\d+)/', $s, $m); preg_replace('/\s+/', ' ', $s); preg_split('/,\s*/', $s);
htmlspecialchars($s, ENT_QUOTES, 'UTF-8');   // экранирование для HTML
md5($s); hash('sha256', $s); password_hash($pw, PASSWORD_DEFAULT); password_verify($pw, $hash);

Массивы

$a = [3, 1, 2];
$a[] = 4; array_push($a, 5); array_pop($a); array_shift($a); array_unshift($a, 0); count($a);
sort($a); rsort($a); usort($a, fn($x, $y) => $x <=> $y); asort($m); ksort($m);
array_map(fn($x) => $x * 2, $a); array_filter($a, fn($x) => $x > 1); array_reduce($a, fn($c, $x) => $c + $x, 0);
array_keys($m); array_values($m); array_merge($a, $b); [...$a, ...$b]; array_slice($a, 1, 2); array_splice($a, 1, 1);
in_array(2, $a, true); array_search(2, $a); array_unique($a); array_reverse($a); array_sum($a); array_column($rows, 'name', 'id');
array_combine($keys, $vals); array_flip($m); array_chunk($a, 2); array_fill(0, 3, 0); range(1, 5); compact('a', 'b'); extract($m);
['a' => $x, 'b' => $y] = $m;  [$p, $q] = [1, 2];

Массив это и список, и словарь. Передаётся по значению (копирование при изменении), для ссылки: &$arr.

Условия и циклы

if ($x > 0) {} elseif ($x === 0) {} else {}
$r = match (true) { $x < 0 => 'минус', $x === 0 => 'ноль', default => 'плюс' };   // 8.0+, строгое сравнение, исчерпывающее
switch ($d) { case 1: break; default: break; }
for ($i = 0; $i < 3; $i++) {}
foreach ($arr as $k => $v) {}   foreach ($arr as &$v) {} unset($v);
while ($cond) {}  do {} while ($cond);  break 2; continue;

Функции

function add(int $a, int $b = 0, int ...$rest): int { return $a + $b + array_sum($rest); }
add(b: 2, a: 1);                          // именованные аргументы (8.0)
$mul = fn($x) => $x * $factor;            // стрелочная, захватывает внешние переменные
$f = function ($x) use ($y, &$z) { };     // замыкание
function gen() { yield 1; yield 2; }      // генератор
function nullable(?string $s = null): ?int {}
function u(int|string $v): static {}      // union types
$callable = strlen(...);                  // first-class callable (8.1)

Классы и ООП

interface Repo { public function find(int $id): ?User; }
trait Loggable { public function log(string $m): void { echo $m; } }

enum Status: string {                      // 8.1
    case Active = 'active'; case Blocked = 'blocked';
    public function label(): string { return ucfirst($this->value); }
}

final class User implements JsonSerializable
{
    use Loggable;
    public const ROLE = 'user';
    private static int $count = 0;

    public function __construct(
        public readonly int $id,                 // property promotion (8.0), readonly (8.1)
        private string $name,
        protected ?string $email = null,
    ) { self::$count++; }

    public function getName(): string { return $this->name; }
    public static function count(): int { return self::$count; }
    public function jsonSerialize(): array { return ['id' => $this->id, 'name' => $this->name]; }
    public function __toString(): string { return $this->name; }
}
readonly class Money { public function __construct(public int $amount) {} }       // 8.2
abstract class Base { abstract protected function run(): void; }
$u = new User(1, 'Аня');   $u instanceof User;   User::class;   static::create();

Магия: __get, __set, __call, __invoke, __clone. Видимость: public, protected, private. Немутабельные объекты и DTO предпочтительнее.

Исключения и ошибки

try { risky(); }
catch (InvalidArgumentException | RuntimeException $e) { echo $e->getMessage(); }
catch (Throwable $e) { error_log((string)$e); throw $e; }
finally { }
throw new DomainException('сообщение', 422, $previous);
set_error_handler(fn($no, $str, $file, $line) => throw new ErrorException($str, 0, $no, $file, $line));

Error (фатальные, TypeError, ValueError) и Exception реализуют Throwable.

Файлы и JSON

file_get_contents('a.txt'); file_put_contents('a.txt', $s, FILE_APPEND | LOCK_EX); file_exists('a.txt'); unlink('a.txt');
$fh = fopen('a.csv', 'r'); while (($row = fgetcsv($fh)) !== false) {} fclose($fh);
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
json_decode($json, true, 512, JSON_THROW_ON_ERROR);
mkdir($dir, 0755, true); scandir($dir); glob('*.php'); pathinfo($p); realpath($p); basename($p);

Работа с базой (PDO) и безопасность

$pdo = new PDO('pgsql:host=localhost;dbname=app', $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$st = $pdo->prepare('SELECT * FROM users WHERE email = :email AND active = :a');
$st->execute(['email' => $email, 'a' => 1]);
$rows = $st->fetchAll();   $row = $st->fetch();   $pdo->lastInsertId();
$pdo->beginTransaction(); $pdo->commit(); $pdo->rollBack();

Никогда не склеивайте SQL из пользовательских данных: только подготовленные запросы (защита от SQL-инъекции). Дальше: htmlspecialchars при выводе (XSS), CSRF-токены, password_hash, HTTPS, session_regenerate_id() после входа, проверка типов и filter_var($e, FILTER_VALIDATE_EMAIL).

Веб: запрос, сессии, ответ

$_GET['q']; $_POST['name']; $_SERVER['REQUEST_METHOD']; $_FILES['f']; $_COOKIE['c']; php://input
$body = json_decode(file_get_contents('php://input'), true);
session_start(); $_SESSION['uid'] = 1; session_destroy();
header('Content-Type: application/json'); http_response_code(201); header('Location: /home'); exit;
setcookie('c', 'v', ['expires' => time() + 3600, 'httponly' => true, 'secure' => true, 'samesite' => 'Lax']);

Конфигурация: php.ini (memory_limit, upload_max_filesize, display_errors=Off в продакшене), .env через vlucas/phpdotenv. Серверы: Nginx + PHP-FPM, Apache, RoadRunner, FrankenPHP, Swoole.

Асинхронность

Классическая модель синхронная, один запрос на процесс. Асинхронность: Fibers (8.1), ReactPHP, AMPHP, Swoole / OpenSwoole, очереди (Laravel Queue, Symfony Messenger, RabbitMQ).

Тесты и качество

final class CalcTest extends PHPUnit\Framework\TestCase
{
    #[PHPUnit\Framework\Attributes\DataProvider('cases')]
    public function testAdd(int $a, int $b, int $exp): void { $this->assertSame($exp, add($a, $b)); }
    public static function cases(): array { return [[1, 2, 3], [0, 0, 0]]; }
}

Pest (it('adds', fn() => expect(add(1, 2))->toBe(3));), PHPUnit, Mockery, Behat. Запуск vendor/bin/phpunit.

Подводные камни

  • == с неожиданными приведениями: используйте ===.
  • strlen и substr на UTF-8: mb_*.
  • Порядок foreach по ссылке: unset($v).
  • empty('0') истинно.
  • Отсутствие declare(strict_types=1).
  • Ошибки не должны выводиться пользователю.
  • include пропущенных файлов: автозагрузка Composer.

Экосистема по задачам

Задача Что взять
Фреймворки Laravel, Symfony, Slim, Yii, CodeIgniter
CMS WordPress, Drupal, Joomla, October
ORM Eloquent, Doctrine, Cycle ORM
HTTP-клиент Guzzle, Symfony HttpClient
Шаблоны Blade, Twig
Очереди Laravel Horizon, Symfony Messenger, php-amqplib
Логи Monolog
Валидация symfony/validator, Respect
Тесты PHPUnit, Pest, Mockery
Анализ PHPStan, Psalm, Rector