PHPのお勉強!

PHP TOP

FAQ: 名前空間について知っておくべきこと

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

この FAQ は 2 つに別れています。一般的な質問と、 深く理解するために有用な実装に関する質問です。

まずは一般的な質問。

  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\namemy\name であり、 \ExceptionException となります。

例5 完全修飾名

<?php
namespace foo;
$a = new \my\name(); // "my\name" クラスのインスタンスを作成します
echo \strlen('hi'); // "strlen" 関数をコールします
$a = \INI_ALL; // $a に定数 "INI_ALL" の値を設定します
?>

my\name のような名前はどのように解決される?

名前にバックスラッシュを含むが先頭はバックスラッシュでない名前、たとえば my\name のような名前は 2 通りの方法で解釈されます。

別の名前に my というエイリアスを指定する import 文がある場合は、そのエイリアスが my\namemy 部分に適用されます。

それ以外の場合は、現在の名前空間が my\name の先頭に付け加えられます。

例6 修飾名

<?php
namespace foo;
use
blah\blah as foo;

$a = new my\name(); // "foo\my\name" クラスのインスタンスを作成します
foo\bar::name(); // "blah\blah\bar" のstaticメソッド "name" をコールします
my\bar(); // "foo\my\bar" 関数をコールします
$a = my\BAR; // $a に定数 "foo\my\BAR" の値を設定します
?>

修飾されていない name のようなクラス名はどのように解決される?

バックスラッシュを含まない name のようなクラス名は 2 通りの方法で解釈されます。

別の名前に name というエイリアスを指定する import 文がある場合は、そのエイリアスが適用されます。

それ以外の場合は、現在の名前空間が name の先頭に付け加えられます。

例7 非修飾クラス名

<?php
namespace foo;
use
blah\blah as foo;

$a = new name(); // "foo\name" クラスのインスタンスを作成します
foo::name(); // "blah\blah" クラスのstaticメソッド "name" をコールします
?>

修飾されていない name のような関数名/定数名はどのように解決される?

バックスラッシュを含まない name のような関数名/定数名は 2 通りの方法で解釈されます。

まず、現在の名前空間が 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'); // "foo\strlen" が存在しないので、グローバル関数 "strlen" をコールします
$arr = array(1,3,2);
$b = sort($arr); // "foo\sort" 関数をコールします
$c = foo(); // calls function "foo\foo" - import is not applied

$a = FOO; // sets $a to value of constant "foo\FOO" - import is not applied
$b = INI_ALL; // sets $b to value of global constant "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; // 名前空間 another のクラス "thing" のインスタンスを作成します。
?>

MyClass クラスが名前空間 my\stuff にあるとはいえ、名前の衝突はありません。 MyClass の定義は別のファイルにあるからです。 しかし、次の例は名前の衝突による致命的なエラーとなります。 MyClass の定義が同じファイル上の use 文で行われているからです。

<?php
namespace my\stuff;
use
another\thing as MyClass;
class
MyClass {} // fatal error: MyClass conflicts with import statement
$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 を定数の値と解釈したという notice が発生します。 修飾あるいは完全修飾形式の定数、つまりバックスラッシュを含む定数の場合、 それが未定義なら致命的なエラーが発生します。

例10 未定義の定数

<?php
namespace bar;
$a = FOO; // notice - undefined constants "FOO" assumed "FOO";
$a = \FOO; // fatal error, undefined namespace constant FOO
$a = Bar\FOO; // fatal error, undefined namespace constant bar\Bar\FOO
$a = \Bar\FOO; // fatal error, undefined namespace constant Bar\FOO
?>

特別な定数 null, true, false は上書きできない

名前空間内で特別な組み込み定数を定義しようとすると、致命的なエラーが発生します。

例11 未定義の定数

<?php
namespace bar;
const
NULL = 0; // 致命的なエラー
const true = 'stupid'; // これも、致命的なエラー
// etc.
?>

add a note

User Contributed Notes 6 notes

up
15
manolachef at gmail dot com
12 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
8 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
theking2 at king dot ma
2 years 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
3
teohad at NOSPAM dot gmail dot com
8 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
7
phpcoder
9 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;
?>