Пространства имён (Namespaces)
Зачем нужны пространства имён
Пространства имён решают две проблемы:
- Конфликт имён — два класса/функции с одинаковым именем из разных библиотек
- Организация кода — логическая группировка связанных классов
<?php
// Without namespaces — name collision!
// class Logger {} // Your class
// class Logger {} // Library class — FATAL ERROR!
// With namespaces — no problem
namespace App\Logging;
class Logger {}
namespace Vendor\Logging;
class Logger {}
Объявление пространства имён
<?php
declare(strict_types=1);
namespace App\Entity;
// Everything in this file belongs to App\Entity namespace
class User
{
public function __construct(
private readonly string $name,
private readonly string $email,
) {
}
}
class Product
{
public function __construct(
private readonly string $title,
private readonly float $price,
) {
}
}
Правила объявления
<?php
// namespace MUST be the first statement in the file
// (only declare() and comments can come before it)
declare(strict_types=1);
namespace App\Service;
// CORRECT: declare before namespace is allowed
<?php
// WRONG: code before namespace
echo 'Hello';
// namespace App\Service; // Fatal error!
Ловушка экзамена:
namespaceдолжен быть первым оператором в файле. Единственное, что может стоять перед ним —declare(strict_types=1);и комментарии. Любой другой код доnamespaceвызовет Fatal Error.
Вложенные пространства имён
<?php
declare(strict_types=1);
// Sub-namespaces use backslash separator
namespace App\Entity\User;
class Profile {}
// Full name: App\Entity\User\Profile
Использование классов из других пространств
Полное квалифицированное имя (FQCN)
<?php
declare(strict_types=1);
namespace App\Service;
class UserService
{
public function createUser(): \App\Entity\User
{
// Backslash at the beginning = absolute path
return new \App\Entity\User('Alice', '[email protected]');
}
public function log(): void
{
// Global namespace functions/classes need leading backslash
$date = new \DateTime();
\var_dump($date);
}
}
Импорт с use
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\User;
use App\Entity\Product;
use App\Repository\UserRepository;
class UserService
{
public function __construct(
private readonly UserRepository $repository,
) {
}
public function createUser(string $name, string $email): User
{
// Now we can use short names
return new User($name, $email);
}
}
Псевдонимы (as)
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\User as AppUser;
use Vendor\Auth\User as AuthUser;
use App\Entity\Product;
class ImportService
{
public function convert(AuthUser $authUser): AppUser
{
return new AppUser(
$authUser->getName(),
$authUser->getEmail(),
);
}
}
Групповой импорт (PHP 7.0+)
<?php
declare(strict_types=1);
namespace App\Controller;
// Group use declarations
use App\Entity\{User, Product, Order};
use App\Service\{UserService, ProductService};
use App\Repository\{
UserRepository,
ProductRepository,
OrderRepository,
};
// Mixed group use (classes, functions, constants)
use App\Utils\{
Helper, // Class
function formatDate, // Function
function formatMoney, // Function
const MAX_RETRIES, // Constant
const DEFAULT_TIMEOUT, // Constant
};
Импорт функций и констант
<?php
declare(strict_types=1);
namespace App\Service;
// Import a function
use function App\Utils\formatDate;
use function App\Utils\formatMoney;
// Import a constant
use const App\Config\MAX_RETRIES;
use const App\Config\DB_HOST;
class PaymentService
{
public function process(): string
{
$date = formatDate(time()); // Imported function
$retries = MAX_RETRIES; // Imported constant
return "Processed at {$date}, max retries: {$retries}";
}
}
Глобальное пространство имён
<?php
declare(strict_types=1);
namespace App\Service;
class FileService
{
public function readFile(string $path): string
{
// Global functions: PHP first checks current namespace,
// then falls back to global
$content = file_get_contents($path); // Works! Falls back to global
// But for classes — NO fallback!
// $dt = new DateTime(); // Error: App\Service\DateTime not found!
$dt = new \DateTime(); // Must use backslash for global classes
// Exception class also needs backslash
throw new \RuntimeException('Error');
// NOT: throw new RuntimeException(); — would look in App\Service\
}
}
Ловушка экзамена: Для функций и констант PHP делает fallback в глобальное пространство имён, если не находит в текущем. Для КЛАССОВ fallback НЕ РАБОТАЕТ — нужно явно указывать
\ClassNameили использоватьuse. Это одно из самых частых заблуждений!
Таблица резолюции имён
| Тип | Полное имя | Относительное | Fallback |
|---|---|---|---|
| Классы | \App\Entity\User |
User (с use) |
НЕТ |
| Функции | \strlen() |
strlen() |
ДА (глобальная) |
| Константы | \PHP_INT_MAX |
PHP_INT_MAX |
ДА (глобальная) |
Автозагрузка (Autoloading)
spl_autoload_register
<?php
declare(strict_types=1);
// Custom autoloader
spl_autoload_register(function (string $className): void {
// Convert namespace to file path
// App\Entity\User => src/Entity/User.php
$path = str_replace('\\', DIRECTORY_SEPARATOR, $className);
$file = __DIR__ . '/src/' . $path . '.php';
if (file_exists($file)) {
require $file;
}
});
// Now classes are loaded automatically
$user = new App\Entity\User('Alice', '[email protected]');
PSR-4 стандарт автозагрузки
Namespace prefix => Base directory
App\ => src/
App\Entity\User => src/Entity/User.php
App\Service\Auth => src/Service/Auth.php
Правила PSR-4:
- Каждый класс — в отдельном файле
- Имя файла совпадает с именем класса (с учётом регистра)
- Пространство имён соответствует структуре директорий
- Используется один базовый каталог для корневого пространства имён
Composer autoload
{
"autoload": {
"psr-4": {
"App\\": "src/",
"Tests\\": "tests/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
<?php
declare(strict_types=1);
// In your bootstrap/entry file
require __DIR__ . '/vendor/autoload.php';
// All PSR-4 compliant classes are now auto-loaded
use App\Service\UserService;
use App\Entity\User;
$service = new UserService();
Запомни: Composer реализует PSR-4 автозагрузку. После изменения секции
autoloadвcomposer.jsonнужно выполнитьcomposer dump-autoloadдля обновления карты классов.
Множественные пространства имён в одном файле
<?php
declare(strict_types=1);
// Method 1: Bracketed syntax (only way for multiple namespaces)
namespace App\Entity {
class User {}
}
namespace App\Service {
use App\Entity\User;
class UserService
{
public function getUser(): User
{
return new User();
}
}
}
// Global namespace code
namespace {
// Code in global namespace
$service = new App\Service\UserService();
}
Ловушка экзамена: Нельзя смешивать простой синтаксис
namespace X;и скобочныйnamespace X { }в одном файле. Если используете несколько namespace в файле — только скобочный синтаксис. На практике — один namespace на файл (PSR-4).
Магические константы пространства имён
<?php
declare(strict_types=1);
namespace App\Entity;
class User
{
public function info(): void
{
echo __NAMESPACE__; // 'App\Entity'
echo __CLASS__; // 'App\Entity\User'
echo __FUNCTION__; // 'info'
echo User::class; // 'App\Entity\User'
echo self::class; // 'App\Entity\User'
echo static::class; // Resolved at runtime (late static binding)
}
}
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\User;
// ::class resolution
echo User::class; // 'App\Entity\User' (fully qualified)
echo UserService::class; // 'App\Service\UserService'
// Dynamic class name (PHP 8.0+)
$obj = new User('test', '[email protected]');
echo $obj::class; // 'App\Entity\User' (works on instances!)
Запомни:
ClassName::classвсегда возвращает полное квалифицированное имя класса (FQCN) в виде строки. Это работает и сuse—use App\Entity\User; echo User::class;вернёт'App\Entity\User', а не просто'User'. С PHP 8.0+ работает и на экземплярах:$obj::class.