Namespaces
May 30, 2017 · View on GitHub
General
A problem encountered when managing large projects is that of avoiding the use of the same name in the same scope for different purposes. This is especially problematic in a language that supports modular design and component libraries.
A namespace is a container for a set of (typically related) definitions of classes, interfaces, traits, functions, and constants. Namespaces serve two purposes:
- They help avoid name collisions.
- They allow certain long names to be accessed via shorter, more convenient and readable, names.
A namespace may have sub-namespaces, where a sub-namespace name shares
a common prefix with another namespace. For example, the namespace
Graphics might have sub-namespaces Graphics\D2 and Graphics\D3, for
two- and three-dimensional facilities, respectively. Apart from their
common prefix, a namespace and its sub-namespaces have no special
relationship. The namespace whose prefix is part of a sub-namespace need
not actually exist for the sub-namespace to exist. That is, NS1\Sub can
exist without NS1.
In the absence of any namespace definition, the names of subsequent classes, interfaces, traits, functions, and constants are in the default namespace, which has no name, per se.
The namespaces HH, PHP, php, and sub-namespaces beginning with those prefixes are reserved for use by Hack.
Name Lookup
When an existing name is used in source code, the Engine must decide how that name is found with respect to namespace lookup. For this purpose, names can have one of the three following forms:
- Unqualified name: Such names are just simple names without any prefix, as in the class name
Pointin the following expression:$p = new Point(3,5). If the current namespace isNS1, the namePointresolves toNS1\Point. If the current namespace is the default namespace, the namePointresolves toPoint. In the case of an unqualified function or constant name, if that name does not exist in the current namespace, a global function or constant by that name is used. - Qualified name: Such names have a prefix consisting of a namespace name and/or one or more levels of sub-namespace names, and, possibly, a class, interface, trait, function, or constant name. Such names are relative. For example,
D2\Pointcould be used to refer to the class Point in the sub-namespaceD2of the current namespace. One special case of this is when the first component of the name is the keywordnamespace. This means "the current namespace". - Fully qualified name: Such names begin with a backslash (
\) and are followed optionally by a namespace name and one or more levels of sub-namespace names, and, finally, a class, interface, trait, function, or constant name. Such names are absolute. For example,\Graphics\D2\Pointcould be used to refer unambiguously to the classPointin namespaceGraphics, sub-namespaceD2.
The names of the standard types that come with PHP (such as Exception), constants (such as
PHP_INT_MAX), and library functions (such as is_null) are defined outside
any namespace. To refer unambiguously to such names, one can prefix them
with a backslash (\), as in \Exception, \PHP_INT_MAX, and \is_null. The names of the standard types that are introduced with Hack (such as Vector and Map), are treated implicitly as belonging to namespace HH. Such names are resolved through a process known as auto importation.
Defining Namespaces
Syntax
namespace-definition:
namespace namespace-name ;
namespace namespace-nameopt { declaration-listopt }
namespace-name:
name
namespace-name \ name
namespace-name-as-a-prefix:
\
\opt namespace-name \
namespace \
namespace \ namespace-name \
qualified-name:
namespace-name-as-a-prefixopt name
Defined elsewhere
Constraints
Except for white space, the first occurrence of a namespace-definition in a script must be the first thing in that script.
All occurrence of a namespace-definition in a script must have the declaration-list form or must have the "semicolon" form; the two forms cannot be mixed.
When a script contains source code that is not inside a namespace, and source code that is inside one or namespaces, the namespaced code must use the declaration-list form of namespace-definition.
The declaration-list must not contain a namespace-definition.
Semantics
Namespace and sub-namespace names are case-preserved.
The pre-defined constant __NAMESPACE__ contains the name of
the current namespace.
When the same namespace is defined in multiple scripts, and those scripts are combined into the same program, the namespace is considered the merger of its individual contributions.
In the "semicolon" form of namespace-definition the given namespace extends until the end of the script, or until the lexically next namespace-definition, whichever comes first. In the declaration-list form the namespace extends from the opening brace to the closing brace.
Examples
Script1.php:
namespace NS1;
... // __NAMESPACE__ is "NS1"
namespace NS3\Sub1;
... // __NAMESPACE__ is "NS3\Sub1"
Script2.php:
namespace NS1
{
... // __NAMESPACE__ is "NS1"
}
namespace
{
... // __NAMESPACE__ is ""
}
namespace NS3\Sub1;
{
... // __NAMESPACE__ is "NS3\Sub1"
}
Namespace Use Declarations**
Syntax
namespace-use-declaration:
use namespace-use-kindopt namespace-use-clauses ;
use namespace-use-kind namespace-name-as-a-prefix { namespace-use-clauses } ;
use namespace-name-as-a-prefix { namespace-use-kind-clauses } ;
namespace-use-clauses:
namespace-use-clause
namespace-use-clauses , namespace-use-clause
namespace-use-clause:
qualified-name namespace-aliasing-clauseopt
namespace-use-kind-clauses:
namespace-use-kind-clause
namespace-use-kind-clauses , namespace-use-kind-clause
namespace-use-kind-clause:
namespace-use-kindopt qualified-name namespace-aliasing-clauseopt
namespace-aliasing-clause:
as name
namespace-use-kind:
function
const
Defined elsewhere
Constraints
A namespace-use-declaration must not occur except at the pseudomain level.
If the same qualified-name is imported multiple times in the same scope, each occurrence must have a different alias.
Semantics
If a namespace-use-kind is specified before the clauses or group prefix, then all subsequent clauses must name constants or functions, as appropriate.
Otherwise, if a namespace-use-kind is specified in a namespace-use-kind-clause then the clause must name a constant or function, as appropriate.
Otherwise, the clause must name a namespace, class, interface or trait.
A namespace-use-declaration imports—that is, makes available—one or more names into a scope, optionally giving them each an alias. Each of those names may designate a namespace, a sub-namespace, a class, an interface, or a trait. If a namespace-alias-clause is present, its name is the alias for qualified-name. Otherwise, the right-most name in qualified-name is the implied alias for qualified-name.
The "group" form of a namespace-use-declaration is a convenient syntax when importing many members of a given namespace. The "group" form logically concatenates the prefix onto the qualified-name in each clause. See the following section for an example.
Examples
namespace NS1 {
function f(): void { … }
class C { … }
interface I { … }
trait T { … }
}
namespace NS2 {
use \NS1\C, \NS1\I, \NS1\T;
class D extends C implements I {
use T;
}
\NS1\f(); // explicit namespace
use \NS1\C as C2; // C2 is an alias for the class name \NS1\C
$c2 = new C2;
}
The namespace-use-declaration in the example above could also be written in "group" form as:
use \NS1\ { C, I, T };