It’s been a bumpy ride, in regards to namespace support in PHP. Thankfully, it was added to the language in PHP 5.3, and the applicable structure of PHP code has improved greatly since then. But how exactly do we use them?
What’s a Namespace?
"Don’t forget to escape the backslash when you store a namespace name in a string!"
Imagine a namespace as a drawer in which you can put all kinds of things: a pencil, a ruler, a piece of paper and so forth. These are your belongings. Directly underneath your drawer is someone else’s, and he puts the same things in it. To avoid using each other’s items, you decide to label the drawers so it’s clear what belongs to whom.
Previously, developers had to use underscores in their classes, functions and constants to separate code bases. That’s equivalent to labeling each others belongings and putting them in one big drawer. Sure, it’s at least some kind of organization, but it’s very inefficient.
Namespacing to the rescue! You can declare the same function, class, interface and constant definitions in separate namespaces without receiving fatal errors. Essentialy, a namespace is nothing more than a hierarchically labeled code block holding regular PHP code.
You are Using Them!
It is important to keep in mind that you indirectly make use of namespaces; as of PHP 5.3, all the definitions which are not yet declared in a user defined namespace fall under the global namespace.
The global namespace also holds all internal PHP definitions, like echo()
, mysqli_connect()
, and the Exception
class. Because the global namespace has no unique identifying name, its most often referred to as the global space.
Note that it’s not an obligation to use namespacing.
Your PHP script will work perfectly fine without them, and this behavior isn’t about to change very soon.
Defining a Namespace
A namespace definition is the first statement the PHP interpreter should encounter in a PHP file. The only statement allowed to occur above a namespace declaration is a declare
statement, and then again, only if it declares the encoding of the script.
Declaring a namespace is as simple as using the namespace
keyword. A namespace name should obey the same rules as other identifiers in PHP. Therefore, a namespace must start with a letter or underscore, followed by any number of letters, numbers, or underscores.
<?php namespace MyProject { // Regular PHP code goes here, anything goes! function run() { echo 'Running from a namespace!'; } }
If you want to assign a code block to the global space, you use the namespace
keyword without appending a name.
<?php namespace { // Global space! }
You are allowed to have multiple namespaces in the same file.
<?php namespace MyProject { } namespace MySecondProject { } namespace { }
You can also scatter the same namespace throughout different files; the process of file inclusion automatically merges them. Therefore, it’s a good coding practice to limit the amount of namespace definitions to one per file, just like you would do with classes.
Namespacing is used to avoid conflicting definitions and introduce more flexibility and organization in your code base.
Note that the brackets surrounding the namespace code block are completely optional. In fact, sticking to the one-namespace-per-file rule and omitting the curly brackets makes your code a lot cleaner–there’s no need to indent the nested code.
Sub-namespaces
Namespaces can follow a certain hierarchy, much like the directories in the file system on your computer. Sub-namespaces are extremely useful for organizing the structure of a project. For example, if your project requires database access, you might want to put all the database-related code, such as a database exception and connection handler, in a sub-namespace called Database
.
To maintain flexibility, it is wise to store sub-namespaces in sub-directories. This encourages structuring of your project and makes it much easier to use autoloaders that follow the PSR-0 standard.
PHP uses the backslash as its namespace separator.
Fun fact: in the RFC to decide which namespace separator should be used, they even considered using a smiley.
// myproject/database/connection.php <?php namespace MyProject\Database class Connection { // Handling database connections }
You can have as many sub-namespaces as you want.
<?php namespace MyProject\Blog\Auth\Handler\Social; class Twitter { // Handles Twitter authentification }
Defining sub-namespaces with nested code blocks is not supported. The following example will throw a very descriptive fatal error: “Namespace declarations cannot be nested”.
<?php namespace MyProject { namespace Database { class Connection { } } }
Calling Code from a Namespace
If you want to instantiate a new object, call a function or use a constant from a different namespace, you use the backslash notation. They can be resolved from three different view points:
- Unqualified name
- Qualified name
- Fully qualified name
Unqualified Name
This is the name of a class, function or constant without including a reference to any namespace whatsoever. If you are new to namespacing, this is the view point you are used to working from.
<?php namespace MyProject; class MyClass { static function static_method() { echo 'Hello, world!'; } } // Unqualified name, resolves to the namespace you are currently in (MyProject\MyClass) MyClass:static_method();
Qualified Name
This is how we access the sub-namespace hierarchy; it makes use of the backslash notation.
<?php namespace MyProject; require 'myproject/database/connection.php'; // Qualified name, instantiating a class from a sub-namespace of MyProject $connection = new Database\Connection();
The example below throws a fatal error: “Fatal error: Class ‘MyProject\Database\MyProject\FileAccess\Input’ not found” because MyProject\FileAccess\Input
is approached relatively to the namespace you are currently in.
<?php namespace MyProject\Database; require 'myproject/fileaccess/input.php'; // Trying to access the MyProject\FileAccess\Input class $input = new MyProject\FileAccess\Input();
Fully Qualified Name
The unqualified and qualified names are both relative to the namespace you are currently in. They can only be used to access definitions on that level or to dive deeper into the namespace hierarchy.
If you want to access a function, class or constant residing at a higher level in the hierarchy, then you need to use the fully qualified name–an absolute path rather than relative. This boils down to prepending your call with a backslash. This lets PHP know that this call should be resolved from the global space instead of approaching it relatively.
<?php namespace MyProject\Database; require 'myproject/fileaccess/input.php'; // Trying to access the MyProject\FileAccess\Input class // This time it will work because we use the fully qualified name, note the leading backslash $input = new \MyProject\FileAccess\Input();
It’s not required to use the fully qualified name of internal PHP functions. Calling an unqualified name for a constant or function which does not exist in the namespace you are currently working in results in PHP searching the global namespace for them. This is a built-in fallback which does not apply to unqualified class names.
With this in mind, we can now overload internal PHP functions whilst still being able to call the original function (or constant for that matter).
<?php namespace MyProject; var_dump($query); // Overloaded \var_dump($query); // Internal // We want to access the global Exception class // The following will not work because there's no class called Exception in the MyProject\Database namespace and unqualified class names do not have a fallback to global space // throw new Exception('Query failed!'); // Instead, we use a single backslash to indicate we want to resolve from global space throw new \Exception('ailed!'); function var_dump() { echo 'Overloaded global var_dump()!<br />'; }
Dynamic calls
PHP is a dynamic programming language; so you can also apply this functionality for calling namespaced code. This is essentially the same as instantiating variable classes or including variable files. The namespace separator PHP uses is also a meta character in strings. Don’t forget to escape the backslash when you store a namespace name in a string!
<?php namespace OtherProject; $project_name = 'MyProject'; $package_name = 'Database'; $class_name = 'Connection'; // Include a variable file require strtolower($project_name . '/'. $package_name . '/' . $class_name) . '.php'; // Name of a variable class in a variable namespace. Note how the backslash is escaped to use it properly $fully_qualified_name = $project_name . '\\' . $package_name . '\\' . $class_name; $connection = new $fully_qualified_name();
The namespace
Keyword
Not only is the namespace
keyword used to define a namespace, it can also be used to explicitly resolve to the current namespace, functionally similar to the self
keyword for classes.
<?php namespace MyProject; function run() { echo 'Running from a namespace!'; } // Resolves to MyProject\run run(); // Explicitly resolves to MyProject\run namespace\run();
The __NAMESPACE__
constant
Much like the self
keyword cannot be used to determine what the current class name is, the namespace
keyword cannot be used to determine what the current namespace is. This is why we have the __NAMESPACE__
constant.
<?php namespace MyProject\Database; // 'MyProject\Database' echo __NAMESPACE__;
This constant is very useful for learning if you are just starting out with namespaces; it is also helpful for debugging. As it is a string, it can also be used in combination with dynamic code calls which we previously discussed.
Aliasing or Importing
"it’s not an obligation to use namespacing"
Namespacing in PHP has support for importing. Importing is also referred to as aliasing. Only classes, interfaces, and namespaces can be aliased or imported.
Importing is a very useful and fundamental aspect of namespacing. It gives you the ability to make use of external code packages, like libraries, without having to worry about conflicting names. Importing is achieved by using the use
keyword. Optionally, you can specify a custom alias with the as
keyword.
use [name of class, interface or namespace] as [optional_custom_alias]
How it’s Done
A fully qualified name can be aliased to a shorter unqualified name so that you don’t have to write its fully qualified name each time you want to make use of it. Aliasing or importing should occur in the highest scope of a namespace or in the global scope. Trying to do this in the scope of a method or function is invalid syntax.
<?php namespace OtherProject; // This holds the MyProject\Database namespace with a Connection class in it require 'myproject/database/connection.php'; // If we want to access the database connection of MyProject, we need to use its fully qualified name as we're in a different name space $connection = new \MyProject\Database\Connection(); // Import the Connection class (it works exactly the same with interfaces) use MyProject\Database\Connection; // Now this works too! Before the Connection class was aliased PHP would not have found an OtherProject\Connection class $connection = new Connection(); // Import the MyProject\Database namespace use MyProject\Database; $connection = new Database\Connection()
Alternatively, you can alias to it a different name:
<?php namespace OtherProject; require 'myproject/database/connection.php'; use MyProject\Database\Connection as MyConnection; $connection = new MyConnection(); use MyProject\Database as MyDatabase; $connection = new MyDatabase\Connection();
You are also allowed to import global classes, like the Exception
class. When imported, you don’t have to write its fully qualified name anymore.
Note that import names are not resolved as relative to the current namespace but from an absolute standpoint, starting at the global space. This means that a leading backslash is unnecessary and not recommended.
<?php namespace MyProject; // Fatal error: Class 'SomeProject\Exception' not found throw new Exception('An exception!'); // OK! throw new \Exception('An exception!'); // Import global Exception. 'Exception' is resolved from an absolute standpoint, the leading backslash is unnecessary use Exception; // OK! throw new Exception('An exception!');
Though it is possible to dynamically call namespaced code, dynamic importing is not supported.
<?php namespace OtherProject; $parser = 'markdown'; // This is valid PHP require 'myproject/blog/parser/' . $parser . '.php'; // This is not use MyProject\Blog\Parser\$parser;
Conclusion
Namespacing is used to avoid conflicting definitions and introduce more flexibility and organization in your code base. Remember that you are not obligated to use namespacing; it’s a feature used in combination with an object oriented workflow. Hopefully, however, you will consider taking your (future) PHP project to the next level by making use of namespacing. Have you decided yet?
No hay comentarios:
Publicar un comentario