Часто задаваемые вопросы (FAQ): вещи, которые вам необходимо знать о пространствах имен

(PHP 5 >= 5.3.0, PHP 7)

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

Сперва, общие вопросы.

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

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

  1. Импортируемые имена не могут конфликтовать с классами, определенными в том же файле.
  2. Вложенные пространства имен недопустимы.
  3. Ни функции, ни константы не могут быть импортированы с помощью оператора use.
  4. Динамические имена пространств имен (идентификаторы, взятые в кавычки) должны экранировать символ обратного слеша.
  5. Ссылаться на неопределенные константы, используя обратный слеш, нельзя. Выводится фатальная ошибка
  6. Невозможно переопределить специальные константы, такие как NULL, TRUE, FALSE, ZEND_THREAD_SAFE или ZEND_DEBUG_BUILD

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

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

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

<?php
$a 
= new \stdClass;
?>

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

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

<?php
$a 
= new stdClass;
?>

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

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

<?php
namespace foo;
$a = new \stdClass;

function 
test(\ArrayObject $typehintexample null) {}

$a = \DirectoryIterator::CURRENT_AS_FILEINFO;

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

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

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

<?php
namespace foo;

class 
MyClass {}

// использование класса из текущего пространства имен
function test(MyClass $typehintexample null) {}
// другой способ использовать класс из текущего пространства имен
function test(\foo\MyClass $typehintexample 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 другого имени, то применяется этот синоним.

В ином случае, текущее имя пространства имен становится префиксом к my\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 {}
}
?>

Ни функции, ни константы не могут быть заимпортированы с помощью оператора use.

Элементы, которые подвержены действию оператора use - это пространства имен и имена классов. Для сокращения длинных имен констант или функций, заимпортируйте их содержимое в пространство имен.

<?php
namespace mine;
use 
ultra\long\ns\name;

$a name\CONSTANT;
name\func();
?>

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

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

Пример #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, ZEND_THREAD_SAFE или ZEND_DEBUG_BUILD

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

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

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

Коментарии

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'10true); // produces notice - Constant null already defined
?>
2012-04-10 17:32:29
http://php5.kiev.ua/manual/ru/language.namespaces.faq.html
Автор:
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 function.define, 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.
2013-07-25 11:31:11
http://php5.kiev.ua/manual/ru/language.namespaces.faq.html
Автор:
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;
?>
2015-03-25 15:01:55
http://php5.kiev.ua/manual/ru/language.namespaces.faq.html
[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()
}
?>
2016-04-24 12:02:03
http://php5.kiev.ua/manual/ru/language.namespaces.faq.html
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_dumpBar::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
2016-10-04 10:20:47
http://php5.kiev.ua/manual/ru/language.namespaces.faq.html
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();
2022-07-09 13:18:10
http://php5.kiev.ua/manual/ru/language.namespaces.faq.html

    Поддержать сайт на родительском проекте КГБ