How to Use PHP Namespaces, Part 2: Importing, Aliases, and Name Resolution

    Craig Buckler
    Share

    PHP namespacesIn part 1, we discussed why PHP namespaces are useful and the namespace keyword. In this article, we examine the use command and the way PHP resolves namespace names.

    For the purposes of this example, we will define two almost identical code blocks; the only difference is their namespace:

    lib1.php:

    
    <?php
    // application library 1
    namespace App\Lib1;
    
    const MYCONST = 'App\Lib1\MYCONST';
    
    function MyFunction() {
    	return __FUNCTION__;
    }
    
    class MyClass {
    	static function WhoAmI() {
    		return __METHOD__;
    	}
    }
    ?>
    

    lib2.php:

    
    <?php
    // application library 2
    namespace App\Lib2;
    
    const MYCONST = 'App\Lib2\MYCONST';
    
    function MyFunction() {
    	return __FUNCTION__;
    }
    
    class MyClass {
    	static function WhoAmI() {
    		return __METHOD__;
    	}
    }
    ?>
    

    There is a little PHP terminology to understand before we begin…

    Fully-qualified name
    Any PHP code can refer to a fully-qualified name — an identifier starting with the namespace backslash separator, e.g. AppLib1MYCONST, AppLib2MyFunction(), etc.

    Fully-qualified names have no ambiguity. The initial backslash operates in a similar way to a file path; it signifies the ‘root’ global space. If we implemented a different MyFunction() in our global space, it could be called from lib1.php or lib2.php using MyFunction().

    Fully-qualified names are useful for one-off function calls or object initialization. However, they can become impractical when you are making lots of calls. As we will discover below, PHP offers other options to save us from namespace typing cramps.

    Qualified name
    An identifier with at least one namespace separator, e.g. Lib1MyFunction().

    Unqualified name
    An identifier without a namespace separator, e.g. MyFunction().

    Working Within the Same Namespace

    Consider the following code:

    myapp1.php:

    
    <?php
    namespace App\Lib1;
    
    require_once('lib1.php');
    require_once('lib2.php');
    
    header('Content-type: text/plain');
    echo MYCONST . "\n";
    echo MyFunction() . "\n";
    echo MyClass::WhoAmI() . "\n";
    ?>
    

    Although we include both lib1.php and lib2.php, the identifiers MYCONST, MyFunction, and MyClass will only reference code in lib1.php. This occurs because the myapp1.php code is within the same AppLib1 namespace:

    result:

    
    App\Lib1\MYCONST
    App\Lib1\MyFunction
    App\Lib1\MyClass::WhoAmI
    

    Namespace Importing

    Namespaces can be imported with the use operator, e.g.

    myapp2.php:

    
    <?php
    use App\Lib2;
    
    require_once('lib1.php');
    require_once('lib2.php');
    
    header('Content-type: text/plain');
    echo Lib2\MYCONST . "\n";
    echo Lib2\MyFunction() . "\n";
    echo Lib2\MyClass::WhoAmI() . "\n";
    ?>
    

    Any number of use statements can be defined or you can separate individual namespaces with a comma. In this example we have imported the AppLib2 namespace. We still cannot refer directly to MYCONST, MyFunction or MyClass because our code is in the global space and PHP will look for them there. However, if we add a prefix of ‘Lib2’, they become qualified names; PHP will search through the imported namespaces until it finds a match.

    result:

    
    App\Lib1\MYCONST
    App\Lib2\MyFunction
    App\Lib2\MyClass::WhoAmI
    

    Namespace Aliases

    Namespace aliases are perhaps the most useful construct. Aliases allow us to reference long namespaces using a shorter name.

    myapp3.php:

    
    <?php
    use App\Lib1 as L;
    use App\Lib2\MyClass as Obj;
    
    header('Content-type: text/plain');
    require_once('lib1.php');
    require_once('lib2.php');
    
    echo L\MYCONST . "\n";
    echo L\MyFunction() . "\n";
    echo L\MyClass::WhoAmI() . "\n";
    echo Obj::WhoAmI() . "\n";
    ?>
    

    The first use statement defines AppLib1 as ‘L’. Any qualified names using ‘L’ will be translated to ‘AppLib1’ at compile-time. We can therefore refer to LMYCONST and LMyFunction rather than the fully-qualified name.

    The second use statement is more interesting. It defines ‘Obj’ as an alias for the class ‘MyClass’ within the AppLib2 namespace. This is only possible for classes — not constants or functions. We can now use new Obj() or run static methods as shown above.

    result:

    
    App\Lib1\MYCONST
    App\Lib1\MyFunction
    App\Lib1\MyClass::WhoAmI
    App\Lib2\MyClass::WhoAmI
    

    PHP Name Resolution Rules

    PHP identifier names are resolved using the following namespace rules. Refer to the PHP manual for more information.

    1. Calls to fully-qualified functions, classes or constants are resolved at compile-time.

    2. Unqualified and qualified names are translated according to the import rules, e.g. if the namespace ABC is imported as C, a call to CDe() is translated to ABCDe().

    3. Inside a namespace, all qualified names not already translated according to import rules have the current namespace prepended, e.g. if a call to CDe() is performed within namespace AB, it is translated to ABCDe().

    4. Unqualified class names are translated according to current import rules and the full name is substituted for short imported name, e.g. if class C in namespace AB is imported as X, new X() is translated to new ABC().

    5. Unqualified function calls within a namespace are resolved at run-time. For example, if MyFunction() is called within namespace AB, PHP first looks for the function ABMyFunction(). If that is not found, it looks for MyFunction() in the global space.

    6. Calls to unqualified or qualified class names are resolved at run-time. For example, if we call new C() within namespace AB, PHP will look for the class ABC. If that is not found, it will attempt to autoload ABC.

    See also:

    In tomorrow’s final article we will cover autoloading and other advanced options.