PHP Velho Oeste 2024

Часто задаваемые вопросы (FAQ): что нужно знать о пространствах имён

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

Этот список вопросов разделён на две части: общие вопросы и некоторые особенности реализации, которые полезны для полного понимания.

Вначале общие вопросы.

  1. Если в коде не указаны пространства имён, нужно ли считать что-либо из этого важным?
  2. Как обращаться к внутренним или глобальным классам в пространстве имён?
  3. Как обращаться к функциям классов в пространствах имён, или константам в их пространстве имён?
  4. Как разрешаются имена \my\name или \name?
  5. Как разрешается имя my\name?
  6. Как разрешается неполное имя класса name?
  7. Как разрешается неполное имя функции или неполное имя константы наподобие name?

Некоторые детали реализации пространств имён, которые полезно понимать.

  1. Импортируемые имена не должны конфликтовать с классами, определёнными в том же файле.
  2. Вложенные пространства имён недопустимы.
  3. Динамические имена пространств имён (идентификаторы, взятые в кавычки) должны экранировать символ обратного слеша.
  4. Ссылаться на неопределённые константы, используя обратный слеш, нельзя. Выводится фатальная ошибка
  5. Невозможно переопределить специальные константы null, true или false

Если в коде не указаны пространства имён, нужно ли считать что-либо из этого важным?

Нет, пространства имён не влияют ни на тот код, который уже написали, ни на ещё ненаписанный код без пространств имён. Разрешается писать такой код, если нужно:

Пример #1 Доступ к глобальным классам вне пространства имён

<?php

$a
= new \stdClass;

?>

Функционально это эквивалентно следующему:

Пример #2 Доступ к глобальным классам вне пространства имён

<?php

$a
= new stdClass;

?>

Как обращаться к внутренним или глобальным классам в пространстве имён?

Пример #3 Доступ ко внутренним классам в пространствах имён

<?php

namespace foo;
$a = new \stdClass;

function
test(\ArrayObject $parameter_type_example = null) {}

$a = \DirectoryIterator::CURRENT_AS_FILEINFO;

// Расширение внутреннего или глобального класса
class MyException extends \Exception {}

?>

Как использовать функции классов в пространствах имён или константы в их собственном пространстве имён?

Пример #4 Доступ ко внутренним классам, функциям или константам в пространствах имён

<?php

namespace foo;

class
MyClass {}

// Определение класса текущего пространства имён в качестве типа параметра
function test(MyClass $parameter_type_example = null) {}

// Другой способ определить класс из текущего пространства имён в качестве типа параметра
function test(\foo\MyClass $parameter_type_example = null) {}

// Расширение класса из текущего пространства имён
class Extended extends MyClass {}

// Доступ к глобальной функции
$a = \globalfunc();

// Доступ к глобальной константе
$b = \INI_ALL;

?>

Как имена вроде \my\name или \name преобразовываются?

Имена, которые начинаются с \, преобразовываются к тому, как они выглядят, т. е. \my\name — это на самом деле my\name, а \Exception — это Exception.

Пример #5 Абсолютные имена

<?php

namespace foo;
$a = new \my\name(); // Создаёт экземпляр класса my\name
echo \strlen('hi'); // Вызывает функцию strlen
$a = \INI_ALL; // Переменной $a присваивается значение константы INI_ALL

?>

Как имя my\name преобразуется?

Имена, которые содержат обратный слеш, но не начинаются с него, например, my\name, — разрешено преобразовывать двумя способами.

Если написано импортирующее выражение, которое создаёт синоним my другого имени, то этот синоним будет применён к my в записи my\name.

В противном случае текущее имя пространства имён становится префиксом к имени my\name.

Пример #6 Полные имена

<?php

namespace foo;
use
blah\blah as foo;

$a = new my\name(); // Создаёт экземпляр класса foo\my\name
foo\bar::name(); // Вызывает статический метод name в классе blah\blah\bar
my\bar(); // Вызывает функцию foo\my\bar
$a = my\BAR; // Присваивает переменной $a значение константы foo\my\BAR

?>

Как неполное имя класса вроде name преобразовывается?

Имена классов без обратного слеша, например name, могут быть преобразованы двумя способами.

Если написано импортирующее выражение, которое создаёт синоним name другого имени, то будет применён этот синоним.

В противном случае текущее имя пространства имён становится префиксом к name.

Пример #7 Неполные имена классов

<?php

namespace foo;
use
blah\blah as foo;

$a = new name(); // Создаёт экземпляр класса foo\name
foo::name(); // Вызывает статический метод name в классе blah\blah

?>

Как неполное имя функции или неполное имя константы, например, name преобразовывается?

Имена функций или констант без обратного слеша, например name, могут быть преобразованы двумя способами.

Сперва текущее имя пространства имён становится префиксом к name.

Затем, если константа или функция name не существует в текущем пространстве имён, будет использована глобальная константа или функция name, если она существует.

Пример #8 Неполные имена функций или констант

<?php

namespace foo;
use
blah\blah as foo;

const
FOO = 1;

function
my() {}
function
foo() {}
function
sort(&$a)
{
\sort($a); // Вызывает глобальную функцию sort
$a = array_flip($a);
return
$a;
}

my(); // вызывает foo\my
$a = strlen('hi'); // Вызывает глобальную функцию strlen, потому что foo\strlen не существует
$arr = array(1,3,2);
$b = sort($arr); // Вызывает функцию foo\sort
$c = foo(); // Вызывает функцию foo\foo — импорт не применяется

$a = FOO; // Присваивает переменной $a значение константы «foo\FOO» — импорт не применяется
$b = INI_ALL; // Присваивает переменной $b значение глобальной константы INI_ALL

?>

Импортируемые имена не должны конфликтовать с классами, определёнными в том же файле.

Следующие комбинации скриптов допустимы:

file1.php

<?php

namespace my\stuff;
class
MyClass {}

?>

another.php

<?php

namespace another;
class
thing {}

?>

file2.php

<?php

namespace my\stuff;
include
'file1.php';
include
'another.php';

use
another\thing as MyClass;
$a = new MyClass; // Создаёт экземпляр класса thing из пространства имён another

?>

Конфликт имён отсутствует, даже несмотря на то что класс MyClass существует внутри пространства имён my\stuff, потому что определение MyClass находится в отдельном файле. Однако следующий пример приводит к фатальной ошибке с конфликтом имён, потому что класс MyClass определён в том же файле, в котором указано ключевое слово use.

<?php

namespace my\stuff;
use
another\thing as MyClass;
class
MyClass {} // Фатальная ошибка: MyClass конфликтует с выражением импорта
$a = new MyClass;

?>

Вложенные пространства имён недопустимы.

PHP не разрешает вложение пространств имён

<?php

namespace my\stuff {
namespace
nested {
class
foo {}
}
}

?>
Однако сымитировать вложенные пространства имён можно так:
<?php

namespace my\stuff\nested {
class
foo {}
}

?>

Динамические имена пространств имён (идентификаторы, взятые в кавычки) должны экранировать символ обратного слеша.

Важно понимать это, потому что обратный слеш внутри строк работает как экранирующий символ. Он должен быть продублирован, когда указан внутри строки, иначе появляется риск неумышленных последствий:

Пример #9 Подводные камни при указании имени пространства имён внутри строки с двойными кавычками

<?php

$a
= "dangerous\name"; // Символ \n — это переход на новую строку внутри строки с двойными кавычками!
$obj = new $a;

$a = 'not\at\all\dangerous'; // А тут нет проблем.
$obj = new $a;

?>
Внутри строк, заключённых в одинарные кавычки, обратный слеш в качестве разделителя более безопасен, но по-прежнему лучше рекомендуемая практика экранирования обратного слеша во всех строках.

Ссылаться на неопределённые константы, используя обратный слеш, нельзя. Выводится фатальная ошибка

Любая неопределённая константа — неполное имя наподобие FOO — будет приводить к выводу сообщения о том, что PHP предположил, что FOO было значением константы. Любая константа, с полным или абсолютным именем, которая содержит символ обратного слеша, будет приводить к фатальной ошибке, если не будет найдена.

Пример #10 Неопределённые константы

<?php

namespace bar;
$a = FOO; // Выводит предупреждение: undefined constants "FOO" assumed "FOO";
$a = \FOO; // Фатальная ошибка: undefined namespace constant FOO
$a = Bar\FOO; // Фатальная ошибка: undefined namespace constant bar\Bar\FOO
$a = \Bar\FOO; // Фатальная ошибка: undefined namespace constant Bar\FOO

?>

Невозможно переопределить специальные константы null, true или false

Любая попытка определить константу пространства имён, которая совпадает с названиями специальных встроенных констант, приведёт к фатальной ошибке.

Пример #11 Неопределённые константы

<?php

namespace bar;
const
NULL = 0; // Фатальная ошибка;
const true = 'stupid'; // Тоже фатальная ошибка;
// и т. д.

?>

add a note

User Contributed Notes 6 notes

up
15
manolachef at gmail dot com
11 years ago
There is a way to define a namespaced constant that is a special, built-in constant, using define function and setting the third parameter case_insensitive to false:

<?php
namespace foo;
define(__NAMESPACE__ . '\NULL', 10); // defines the constant NULL in the current namespace
var_dump(NULL); // will show 10
var_dump(null); // will show NULL
?>

No need to specify the namespace in your call to define(), like it happens usually
<?php
namespace foo;
define(INI_ALL, 'bar'); // produces notice - Constant INI_ALL already defined. But:

define(__NAMESPACE__ . '\INI_ALL', 'bar'); // defines the constant INI_ALL in the current namespace
var_dump(INI_ALL); // will show string(3)"bar". Nothing unespected so far. But:

define('NULL', 10); // defines the constant NULL in the current namespace...
var_dump(NULL); // will show 10
var_dump(null); // will show NULL
?>

If the parameter case_insensitive is set to true
<?php
namespace foo;
define (__NAMESPACE__ . '\NULL', 10, true); // produces notice - Constant null already defined
?>
up
7
shaun at slickdesign dot com dot au
7 years ago
When creating classes or calling static methods from within namespaces using variables, you need to keep in mind that they require the full namespace in order for the appropriate class to be used; you CANNOT use an alias or short name, even if it is called within the same namespace. Neglecting to take this into account can cause your code to use the wrong class, throw a fatal missing class exception, or throw errors or warnings.

In these cases, you can use the magic constant __NAMESPACE__, or specify the full namespace and class name directly. The function class_exists also requires the full namespace and class name, and can be used to ensure that a fatal error won't be thrown due to missing classes.

<?php

namespace Foo;
class
Bar {
public static function
test() {
return
get_called_class();
}
}

namespace
Foo\Foo;
class
Bar extends \Foo\Bar {
}

var_dump( Bar::test() ); // string(11) "Foo\Foo\Bar"

$bar = 'Foo\Bar';
var_dump( $bar::test() ); // string(7) "Foo\Bar"

$bar = __NAMESPACE__ . '\Bar';
var_dump( $bar::test() ); // string(11) "Foo\Foo\Bar"

$bar = 'Bar';
var_dump( $bar::test() ); // FATAL ERROR: Class 'Bar' not found or Incorrect class \Bar used
up
3
teohad at NOSPAM dot gmail dot com
7 years ago
[Editor's note: that behavior is caused by a bug in PHP 7.0, which has been fixed as of PHP 7.0.7.]

Regarding the entry "Import names cannot conflict with classes defined in the same file".
- I found that since PHP 7.0 this is no longer the case.
In PHP 7.0 you can have a class with a name that matches an imported class (or namespace or both at the same time).

<?php
namespace ns1 {
class
ns1 {
public static function
write() {
echo
"ns1\\ns1::write()\n";
}
}
}

namespace
ns1\ns1 {
class
ns1c {
public static function
write() {
echo
"ns1\\ns1\\ns1c::write()\n";
}
}
}

namespace
ns2 {
use
ns1\ns1 as ns1; // both a class in ns1, and a namespace ns1\ns1

// the next class causes fatal error in php 5.6, not in 7.0
class ns1 {
public static function
write() {
echo
"ns2\\ns1::write()\n";
}
}

ns1::write(); // calls imported ns1\ns1::write()
ns1\ns1c::write(); // calls imported ns1\ns1\ns1c::write()
namespace\ns1::write(); // calls ns2\ns1::write()
}
?>
up
2
theking2 at king dot ma
1 year ago
Just like class names currently namespaces are not case sensitive. So no errors will be shown here:

<?php declare(strict_types=1);
namespace
Foo;
class
Bar {
public function
__construct() {
echo
'Map constructed';
}
}

$foobar = new \foo\bar();
up
6
phpcoder
8 years ago
Regarding "Neither functions nor constants can be imported via the use statement." Actually you can do it in PHP 5.6+:

<?php

// importing a function (PHP 5.6+)
use function My\Full\functionName;

// aliasing a function (PHP 5.6+)
use function My\Full\functionName as func;

// importing a constant (PHP 5.6+)
use const My\Full\CONSTANT;
?>
up
-4
okaresz
10 years ago
To correct manolachef's answer: define() ALWAYS defines constants in the GLOBAL namespace.

As nl-x at bita dot nl states in the note at http://www.php.net/manual/en/function.define.php, the constant "NULL" can be defined with define() case-sensitively, but can only be retrieved with constant(), leaving the meaning of NULL uppercase keyword as the only value of the type null.
To Top