PHP 名前空間(namespace)

PHP 名前空間(namespace)は PHP 5.3 で追加されました。目的は名前の重複問題を解決することです。PHP では2つの関数またはクラスが同じ名前を持つことは許されておらず、そうでない場合は致命的なエラーが発生します。

PHP 名前空間は以下の2種類の問題を解決できます:

  1. ユーザーが作成したコードと、PHP 内部のクラス/関数/定数またはサードパーティのクラス/関数/定数との間の名前の衝突。
  2. 非常に長い識別子名(通常は最初の種類の問題を緩和するために定義されます)に対して、エイリアス(または短い)名前を作成し、ソースコードの可読性を向上させます。

名前空間の定義

デフォルトでは、すべての定数、クラス、関数名はグローバル空間に配置されます。これは PHP が名前空間をサポートする前と同じです。

名前空間はキーワード namespace で宣言します。ファイルに名前空間が含まれる場合、他のすべてのコードより前に名前空間を宣言する必要があります。

構文形式は次のとおりです:

<?php  
// 定义代码在 'MyProject' 命名空间中  
namespace MyProject;  
 
// ... 代码 ...  

同じファイル内で異なる名前空間のコードを定義することもできます。例:

<?php  
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
?>  

単一のファイルで複数の名前空間を定義するためにこの構文を使用することは推奨されません。以下の波括弧形式の構文を使用することを推奨します。

<?php
namespace MyProject {
    const CONNECT_OK = 1;
    class Connection { /* ... */ }
    function connect() { /* ... */  }
}

namespace AnotherProject {
    const CONNECT_OK = 1;
    class Connection { /* ... */ }
    function connect() { /* ... */  }
}
?>

グローバルの非名前空間コードと名前空間内のコードを組み合わせるには、波括弧形式の構文のみを使用できます。グローバルコードは、名前のない namespace 文と波括弧で囲む必要があります。例:

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

名前空間を宣言する前に唯一合法なコードは、ソースファイルのエンコーディング方式を定義するための declare 文です。空白文字を含むすべての非 PHP コードは、名前空間の宣言より前に記述することはできません。

<?php
declare(encoding='UTF-8'); //定义多个命名空间和不包含在命名空间中的代码
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // 全局代码
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}
?>

以下のコードでは構文エラーが発生します:

<html>
<?php
namespace MyProject; // 命名空间前出现了“<html>” 会致命错误 - 命名空间必须是程序脚本的第一条语句
?>

サブ名前空間

ディレクトリとファイルの関係によく似ており、PHP 名前空間も階層化された名前空間名を指定できます。したがって、名前空間の名前は階層的な方法で定義できます:

<?php
namespace MyProject\Sub\Level;  //声明分层次的单个命名空间

const CONNECT_OK = 1;
class Connection { /* ... */ }
function Connect() { /* ... */  }

?>

上記の例では、定数 MyProject\Sub\Level\CONNECT_OK、クラス MyProject\Sub\Level\Connection、関数 MyProject\Sub\Level\Connect を作成しています。


名前空間の使用

PHP 名前空間内のクラス名は、次の3つの方法で参照できます:

  1. 非修飾名、またはプレフィックスを含まないクラス名。例:$a=new foo(); または foo::staticmethod();。現在の名前空間が currentnamespace の場合、foo は currentnamespace\foo として解決されます。foo を使用するコードがグローバルで、いかなる名前空間にも含まれないコードの場合、foo は foo として解決されます。警告:名前空間内に関数または定数が未定義の場合、その非修飾の関数名または定数名はグローバル関数名または定数名として解決されます。

  2. 修飾名、またはプレフィックスを含む名前。例:$a = new subnamespace\foo(); または subnamespace\foo::staticmethod();。現在の名前空間が currentnamespace の場合、foo は currentnamespace\subnamespace\foo として解決されます。foo を使用するコードがグローバルで、いかなる名前空間にも含まれないコードの場合、foo は subnamespace\foo として解決されます。

  3. 完全修飾名、またはグローバルプレフィックス演算子を含む名前。例:$a = new \currentnamespace\foo(); または \currentnamespace\foo::staticmethod();。この場合、foo は常にコード内のリテラル名(literal name)currentnamespace\foo として解決されます。

以下はこれら3つの方法を使用した例です:

file1.php ファイルコード

<?php
namespace Foo\Bar\subnamespace; 

const FOO = 1;
function foo() {}
class foo
{
    static function staticmethod() {}
}
?>

file2.php ファイルコード

<?php
namespace Foo\Bar;
include 'file1.php';

const FOO = 2;
function foo() {}
class foo
{
    static function staticmethod() {}
}

/* 非限定名称 */
foo(); // 解析为函数 Foo\Bar\foo
foo::staticmethod(); // 解析为类 Foo\Bar\foo ,方法为 staticmethod
echo FOO; // 解析为常量 Foo\Bar\FOO

/* 限定名称 */
subnamespace\foo(); // 解析为函数 Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod(); // 解析为类 Foo\Bar\subnamespace\foo,
                                  // 以及类的方法 staticmethod
echo subnamespace\FOO; // 解析为常量 Foo\Bar\subnamespace\FOO
                                  
/* 完全限定名称 */
\Foo\Bar\foo(); // 解析为函数 Foo\Bar\foo
\Foo\Bar\foo::staticmethod(); // 解析为类 Foo\Bar\foo, 以及类的方法 staticmethod
echo \Foo\Bar\FOO; // 解析为常量 Foo\Bar\FOO
?>

任意のグローバルクラス、関数、定数にアクセスするには、完全修飾名を使用できることに注意してください。例:\strlen() や \Exception や \INI_ALL。

名前空間内でグローバルクラス、関数、定数にアクセスする:

<?php
namespace Foo;

function strlen() {}
const INI_ALL = 3;
class Exception {}

$a = \strlen('hi'); // 调用全局函数strlen
$b = \INI_ALL; // 访问全局常量 INI_ALL
$c = new \Exception('error'); // 实例化全局类 Exception
?>

名前空間と動的言語の特徴

PHP 名前空間の実装は、言語自体の動的な特徴の影響を受けます。したがって、以下のコードを名前空間に変換する場合は、要素に動的にアクセスします。

example1.php ファイルコード:

<?php
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "global";

$a = 'classname';
$obj = new $a; // prints classname::__construct
$b = 'funcname';
$b(); // prints funcname
echo constant('constname'), "\n"; // prints global
?>

完全修飾名(名前空間プレフィックスを含むクラス名)を使用する必要があります。動的なクラス名、関数名、定数名では、修飾名と完全修飾名に違いがないため、先頭のバックスラッシュは不要であることに注意してください。

名前空間の要素への動的アクセス

<?php
namespace namespacename;
class classname
{
    function __construct()
    {
        echo __METHOD__,"\n";
    }
}
function funcname()
{
    echo __FUNCTION__,"\n";
}
const constname = "namespaced";

include 'example1.php';

$a = 'classname';
$obj = new $a; // 输出 classname::__construct
$b = 'funcname';
$b(); // 输出函数名
echo constant('constname'), "\n"; // 输出 global

/* 如果使用双引号,使用方法为 "\\namespacename\\classname"*/
$a = '\namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$a = 'namespacename\classname';
$obj = new $a; // 输出 namespacename\classname::__construct
$b = 'namespacename\funcname';
$b(); // 输出 namespacename\funcname
$b = '\namespacename\funcname';
$b(); // 输出 namespacename\funcname
echo constant('\namespacename\constname'), "\n"; // 输出 namespaced
echo constant('namespacename\constname'), "\n"; // 输出 namespaced
?>

namespaceキーワードと__NAMESPACE__定数

PHP は現在の名前空間内の要素にアクセスするための2つの抽象的な方法をサポートしています。__NAMESPACE__ マジック定数と namespace キーワードです。

定数 __NAMESPACE__ の値は、現在の名前空間名を含む文字列です。グローバルで、どの名前空間にも含まれないコードでは、空の文字列を含みます。

__NAMESPACE__ の例、名前空間内のコード

<?php
namespace MyProject;

echo '"', __NAMESPACE__, '"'; // 输出 "MyProject"
?>

__NAMESPACE__ の例、グローバルコード

<?php

echo '"', __NAMESPACE__, '"'; // 输出 ""
?>

定数 __NAMESPACE__ は名前を動的に作成するときに便利です。例:

__NAMESPACE__ を使用した名前の動的作成

<?php
namespace MyProject;

function get($classname)
{
    $a = __NAMESPACE__ . '\\' . $classname;
    return new $a;
}
?>

キーワード namespace は、現在の名前空間またはサブ名前空間内の要素に明示的にアクセスするために使用できます。これはクラス内の self 演算子に相当します。

namespace演算子、名前空間内のコード

<?php
namespace MyProject;

use blah\blah as mine; // 引入了 blah\blah 命名空间,并定义了个别名mine

mine\mine(); // 调用函数 blah\blah\mine()
namespace\blah\mine(); // 调用函数 MyProject\blah\mine()

namespace\func(); // 调用函数 MyProject\func()
namespace\sub\func(); // 调用函数 MyProject\sub\func()
namespace\cname::method(); // 调用 MyProject\cname 类的静态方法
$a = new namespace\sub\cname(); // 实例化 MyProject\sub\cname 类的对象
$b = namespace\CONSTANT; // 将常量 MyProject\CONSTANT 的值赋给 $b
?>

namespace演算子、グローバルコード

<?php

namespace\func(); // calls function func()
namespace\sub\func(); // calls function sub\func()
namespace\cname::method(); // calls static method "method" of class cname
$a = new namespace\sub\cname(); // instantiates object of class sub\cname
$b = namespace\CONSTANT; // assigns value of constant CONSTANT to $b
?>

名前空間の使用:エイリアス/インポート

PHP 名前空間は、エイリアスまたはインポートを使用する2つの方法をサポートしています:クラス名にエイリアスを使用するか、名前空間名にエイリアスを使用します。

PHP では、エイリアスは演算子 use によって実現されます。以下は、可能な3つのインポート方法をすべて使用した例です:

1、use演算子を使用したインポート/エイリアスの使用

<?php
namespace foo;
use My\Full\Classname as Another;

// 下面的例子与 use My\Full\NSname as NSname 相同
use My\Full\NSname;

// 导入一个全局类
use \ArrayObject;

$obj = new namespace\Another; // 实例化 foo\Another 对象
$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
$a = new ArrayObject(array(1)); // 实例化 ArrayObject 对象
// 如果不使用 "use \ArrayObject" ,则实例化一个 foo\ArrayObject 对象
?>

2、1行に複数のuse文を含める

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化 My\Full\Classname 对象
NSname\subns\func(); // 调用函数 My\Full\NSname\subns\func
?>

インポート操作はコンパイル時に実行されますが、動的なクラス名、関数名、定数名はそうではありません。

3、インポートと動的な名前

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a;      // 实际化一个 Another 对象
?>

また、インポート操作は非修飾名と修飾名にのみ影響します。完全修飾名は確定しているため、インポートの影響を受けません。

4、インポートと完全修飾名

<?php
use My\Full\Classname as Another, My\Full\NSname;

$obj = new Another; // 实例化 My\Full\Classname 类
$obj = new \Another; // 实例化 Another 类
$obj = new Another\thing; // 实例化 My\Full\Classname\thing 类
$obj = new \Another\thing; // 实例化 Another\thing 类
?>

名前空間の使用:フォールバックするグローバル関数/定数

名前空間内で、PHP が非修飾のクラス、関数、または定数名に遭遇した場合、その名前を解決するために異なる優先戦略を使用します。クラス名は常に現在の名前空間内の名前に解決されます。したがって、システム内部または名前空間に含まれないクラス名にアクセスする場合は、完全修飾名を使用する必要があります。例:

1、名前空間内でグローバルクラスにアクセスする

<?php
namespace A\B\C;
class Exception extends \Exception {}

$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是类 Exception 的一个对象

$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类
?>

関数と定数については、現在の名前空間に関数または定数が存在しない場合、PHP は代わりにグローバル空間の関数または定数を使用します。

2、名前空間内でフォールバックするグローバル関数/定数

<?php
namespace A\B\C;

const E_ERROR = 45;
function strlen($str)
{
    return \strlen($str) - 1;
}

echo E_ERROR, "\n"; // 输出 "45"
echo INI_ALL, "\n"; // 输出 "7" - 使用全局常量 INI_ALL

echo strlen('hi'), "\n"; // 输出 "1"
if (is_array('hi')) { // 输出 "is not array"
    echo "is array\n";
} else {
    echo "is not array\n";
}
?>

グローバル空間

名前空間が定義されていない場合、すべてのクラスと関数の定義はグローバル空間にあります。これは PHP が名前空間の概念を導入する前と同じです。名前の前にプレフィックス \ を付けると、その名前がグローバル空間の名前であることを示します。その名前が他の名前空間にある場合でも同様です。

グローバル空間の使用説明

<?php
namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // 调用全局的fopen函数
     return $f;
} 
?>

名前空間の順序

名前空間が導入されて以来、最もエラーを起こしやすいのはクラスを使用するときであり、このクラスの探索パスがどのようになっているかです。

<?php
namespace A;
use B\D, C\E as F;

// 函数调用

foo();      // 首先尝试调用定义在命名空间"A"中的函数foo()
            // 再尝试调用全局函数 "foo"

\foo();     // 调用全局空间函数 "foo" 

my\foo();   // 调用定义在命名空间"A\my"中函数 "foo" 

F();        // 首先尝试调用定义在命名空间"A"中的函数 "F" 
            // 再尝试调用全局函数 "F"

// 类引用

new B();    // 创建命名空间 "A" 中定义的类 "B" 的一个对象
            // 如果未找到,则尝试自动装载类 "A\B"

new D();    // 使用导入规则,创建命名空间 "B" 中定义的类 "D" 的一个对象
            // 如果未找到,则尝试自动装载类 "B\D"

new F();    // 使用导入规则,创建命名空间 "C" 中定义的类 "E" 的一个对象
            // 如果未找到,则尝试自动装载类 "C\E"

new \B();   // 创建定义在全局空间中的类 "B" 的一个对象
            // 如果未发现,则尝试自动装载类 "B"

new \D();   // 创建定义在全局空间中的类 "D" 的一个对象
            // 如果未发现,则尝试自动装载类 "D"

new \F();   // 创建定义在全局空间中的类 "F" 的一个对象
            // 如果未发现,则尝试自动装载类 "F"

// 调用另一个命名空间中的静态方法或命名空间函数

B\foo();    // 调用命名空间 "A\B" 中函数 "foo"

B::foo();   // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
            // 如果未找到类 "A\B" ,则尝试自动装载类 "A\B"

D::foo();   // 使用导入规则,调用命名空间 "B" 中定义的类 "D" 的 "foo" 方法
            // 如果类 "B\D" 未找到,则尝试自动装载类 "B\D"

\B\foo();   // 调用命名空间 "B" 中的函数 "foo" 

\B::foo();  // 调用全局空间中的类 "B" 的 "foo" 方法
            // 如果类 "B" 未找到,则尝试自动装载类 "B"

// 当前命名空间中的静态方法或函数

A\B::foo();   // 调用命名空间 "A\A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\A\B" 未找到,则尝试自动装载类 "A\A\B"

\A\B::foo();  // 调用命名空间 "A" 中定义的类 "B" 的 "foo" 方法
              // 如果类 "A\B" 未找到,则尝试自动装载类 "A\B"
?>

名前の解決は次のルールに従います:

  1. 完全修飾名の関数、クラス、定数の呼び出しはコンパイル時に解決されます。例:new \A\Bクラスとして解決されるA\B。
  2. すべての非修飾名と修飾名(非完全修飾名)は、現在のインポートルールに従ってコンパイル時に変換されます。例えば、名前空間A\B\Cが次のようにインポートされC、そのC\D\e()の呼び出しは次のように変換されますA\B\C\D\e()。
  3. 名前空間内では、インポートルールによって変換されなかったすべての修飾名は、その前に現在の名前空間名が付加されます。例えば、名前空間A\B内でC\D\e()を呼び出すと、C\D\e()は次のように変換されますA\B\C\D\e() 。
  4. 非限定クラス名は、現在のインポートルールに基づいてコンパイル時に変換されます(短いインポート名の代わりに完全名を使用します)。例えば、名前空間がA\B\CCとしてインポートされた場合、new C()は変換されますnew A\B\C() 。
  5. 名前空間内部(例: A\B)では、非限定名の関数呼び出しは実行時に解決されます。例えば関数foo()の呼び出しは次のように解決されます:
    1. 現在の名前空間でA\B\foo()という名前の関数を検索します
    2. を検索して呼び出そうとしますグローバル(global)空間内の関数foo()。
  6. 名前空間(例:A\B)内部での非限定名または限定名のクラス(完全修飾名ではない)の呼び出しは実行時に解決されます。以下はnew C() 及 new D\E()の解決プロセスです: new C()の解決:
    1. 現在の名前空間でA\B\Cクラスを検索します。
    2. クラスの自動ロードを試みますA\B\C。
    new D\E()の解決:
    1. クラス名の前に現在の名前空間名を付けると次のようになります:A\B\D\E、その後そのクラスを検索します。
    2. クラスの自動ロードを試みますA\B\D\E。
    グローバル名前空間内のグローバルクラスを参照するには、完全修飾名を使用する必要がありますnew \C()。
その他の拡張