Предыдущие версии

До PHP 5.3 весь код жил в глобальном пространстве имён — функции, классы, константы. Два библиотечных класса с именем Logger — и сайт падает с Fatal error.

Неймспейсы и трейты — не «синтаксический сахар». Это реакция на рост проектов и необходимость композиции.

PHP 5.2(2006) - мир без неймспейсов

Проблема: конфликты имён в крупных проектах.
Решение: «венгерская нотация» и префиксы:

				
					// Библиотека A
class MyLib_Logger { ... }
function MyLib_log($msg) { ... }

// Библиотека B
class Other_Logger { ... }  // пришлось переименовать вручную
				
			

→ Тяжело поддерживать, не масштабируется.

 

PHP 5.3 (30.06.2009) - неймспейсы

Что изменилось:

  • Появился namespace, use, __NAMESPACE__.
  • Классы/функции/константы можно группировать.
				
					// Файл: src/Logger/FileLogger.php
class MyProject_Logger_FileLogger { ... }
				
			

После:

				
					// Файл: src/Logger/FileLogger.php
namespace MyProject\Logger;

class FileLogger { ... }  // имя короче, логичнее
				
			

Импорт:

				
					use MyProject\Logger\FileLogger;
use MyProject\DB as Database;  // алиас

$logger = new FileLogger();
$db = new Database\Connection();
				
			

Важно: неймспейсы — компиляционная фича. В рантайме имя класса = MyProject\Logger\FileLogger (строка).

🐘 Позднее статическое связывание и замыкания (PHP 5.3)

1. Позднее статическое связывание: static:: вместо self::

До PHP 5.3: self:: ссылался на класс, где метод определён, а не где вызван.

Было (проблема наследования):

class A {
    public static function who() { echo __CLASS__; }
    public static function test() { self::who(); }  // ← всегда A
}
class B extends A {
    public static function who() { echo __CLASS__; }
}
B::test(); // → выводит "A", а ожидалось "B"

Стало (PHP 5.3+):

class A {
    public static function who() { echo __CLASS__; }
    public static function test() { static::who(); }  // ← late static binding
}
class B extends A {
    public static function who() { echo __CLASS__; }
}
B::test(); // → "B" ✅
💡 static:: — не «магия», а ссылка на класс, из которого вызван метод (runtime), а не где он объявлен (compile time).

2. Анонимные функции и замыкания

До PHP 5.3 — только именованные функции и create_function() (небезопасно, медленно).

PHP 5.3+: анонимные функции с use

$multiplier = 3;
$double = function($n) use ($multiplier) {
    return $n * $multiplier;
};
echo $double(5); // 15

→ use захватывает переменные по значению. Для захвата по ссылке — use (&$var).

Практическое применение: коллбэки
$users = [
    ['name' => 'Иван', 'age' => 28],
    ['name' => 'Анна', 'age' => 24]
];

// Сортировка по возрасту
usort($users, function($a, $b) {
    return $a['age'] <=> $b['age'];
});
⚠️ Ловушка: внутри замыкания $this недоступен (добавили только в PHP 5.4 через $func = function() { ... }; $func = $func->bindTo($this);).

PHP 5.4 (1.03.2012) - трейты

Проблема: множественное наследование запрещено, но код дублируется (например, log(), validate() в разных классах).

 

Решение до трейтов:

  • Копипаст,
  • «миксины» через __call(),
  • наследование от «базового» класса (нарушает SRP).
 

После трейтов:

				
					trait Loggable {
    protected function log(string $msg): void {
        file_put_contents('app.log', "$msg\n", FILE_APPEND);
    }
}

trait Validatable {
    protected function validate(array $data): bool {
        return !empty($data);
    }
}

class User {
    use Loggable, Validatable;

    public function save(array $data) {
        if ($this->validate($data)) {
            $this->log('Saving user');
            // ...
        }
    }
}
				
			

Короткий синтаксис массивов

Было:

				
					$arr = array(1, 2, 3);
$assoc = array('a' => 1, 'b' => 2);
				
			

Стало:

				
					$arr = [1, 2, 3];
$assoc = ['a' => 1, 'b' => 2];
				
			

То же самое в JSON-подобном синтаксисе. Поддерживается везде: return, foreach, array_merge([...])

Доступ к элементу сразу после создания

Было:

				
					$tmp = explode(',', 'a,b,c');
echo $tmp[1]; // b
				
			

Стало:

				
					echo explode(',', 'a,b,c')[1]; // b
				
			

Работает также для:

  • вызова функции → getArray()[0],
  • создания массива → [1,2,3][1],
  • получения свойства → (new User)->name.
 

⚠️ Не работает для констант массивов (MY_CONST[0]) — добавили только в PHP 5.5.

Встроенный веб-сервер (для разработки)

Запуск:

				
					php -S localhost:8000
				
			

— сервер стартует в текущей директории, index.php — точка входа.
Для кастомного роутинга — router.php::

				
					php -S localhost:8000 router.php
				
			

📌 Только для локальной разработки. Не использовать в продакшене.

Ограничения(чтобы не стрелять в ногу)

Фича Ловушка Как избежать
namespace new Logger() ≠ new \Logger() — ищет в текущем неймспейсе Всегда используй use или \ для глобальных классов
trait Коллизия методов: use A, B, если оба имеют log() use A, B { A::log insteadof B; B::log as logBackup; }
Неймспейсы + автозагрузка MyProject\Logger → ожидает файл MyProject/Logger.php Используй PSR-4:
"MyProject\\": "src/" в composer.json

PHP 5.5(20.05.2013) Генераторы, finally

🌀 Генераторы (yield)

Зачем: итерация по большим данным без загрузки всего в память.
Пример:

				
					function xrange($start, $end) {
    for ($i = $start; $i <= $end; $i++) {
        yield $i; // возвращает значение, но сохраняет состояние
    }
}
foreach (xrange(1, 1000000) as $n) { /* не жрёт память */ }
				
			

🛑 finally в try-catch

Было: нет гарантированного выполнения после try/catch.
Стало:

				
					try {
    // ...
} catch (Exception $e) {
    // ...
} finally {
    // Всегда выполнится — даже при return в try/catch
    $db->close();
}
				
			

🏷️ ::class — магическая константа класса

Было:

				
					$className = 'My\\Namespace\\User'; // строка → ошибка при рефакторинге
				
			

Стало:

				
					$className = User::class; // → "My\Namespace\User", проверяется на этапе компиляции
				
			

Работает для любого класса, даже если не загружен (не требует use).

 

🔐 Встроенные функции для паролей

Проблема: md5(), sha1() — небезопасны; ручная соль — ошибка.
Решение:

				
					// Хэширование
$hash = password_hash($password, PASSWORD_DEFAULT);

// Проверка
if (password_verify($input, $hash)) { /* OK */ }

// Проверка устаревания (например, сменился алгоритм)
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
    $hash = password_hash($password, PASSWORD_DEFAULT);
}
				
			

→ Автоматическая соль, адаптивная стоимость, защита от тайминг-атак.