Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a PHP function defined in another namespace without the prefix

When you define a function in a namespace,

namespace foo {     function bar() { echo "foo!\n"; }     class MyClass { } } 

you must specify the namespace when calling it from another (or global) namespace:

bar();          // call to undefined function \bar() foo\bar();      // ok 

With classes you can employ the "use" statement to effectively import a class into the current namespace [Edit: I thought you could "use foo" to get the classes, but apparently not.]

use foo\MyClass as MyClass; new MyClass();  // ok, instantiates foo\MyClass 

but this doesn't work with functions [and would be unwieldy given how many there are]:

use foo\bar as bar; bar();          // call to undefined function \bar() 

You can alias the namespace to make the prefix shorter to type,

use foo as f;   // more useful if "foo" were much longer or nested f\bar();        // ok 

but is there any way to remove the prefix entirely?

Background: I'm working on the Hamcrest matching library which defines a lot of factory functions, and many of them are designed to be nested. Having the namespace prefix really kills the readability of the expressions. Compare

assertThat($names,      is(anArray(         equalTo('Alice'),          startsWith('Bob'),          anything(),          hasLength(atLeast(12))     ))); 

to

use Hamcrest as h; h\assertThat($names,      h\is(h\anArray(         h\equalTo('Alice'),          h\startsWith('Bob'),          h\anything(),          h\hasLength(h\atLeast(12))     ))); 
like image 388
David Harkness Avatar asked Jul 28 '10 22:07

David Harkness


2 Answers

PHP 5.6 will allow to import functions with the use keyword:

namespace foo\bar {     function baz() {         echo 'foo.bar.baz';     } }  namespace {     use function foo\bar\baz;     baz(); } 

See the RFC for more information: https://wiki.php.net/rfc/use_function

like image 196
Matthieu Napoli Avatar answered Oct 06 '22 16:10

Matthieu Napoli


By adding the helper hacks mentioned below, you can import everything from Hamcrest namespace to current namespace by calling:

import_namespace('Hamcrest', __NAMESPACE__); 

Here are the hacks, function_alias works like http://www.php.net/manual/en/function.class-alias.php except if works on functions:

function function_alias ($original, $alias) {    $args = func_get_args();   assert('count($args) == 2', 'function_alias(): requires exactly two arguments');   assert('is_string($original) && is_string($alias)', 'function_alias(): requires string arguments');    // valid function name - http://php.net/manual/en/functions.user-defined.php   assert('preg_match(\'/^[a-zA-Z_\x7f-\xff][\\\\\\\\a-zA-Z0-9_\x7f-\xff]*$/\', $original) > 0', "function_alias(): '$original' is not a valid function name");   assert('preg_match(\'/^[a-zA-Z_\x7f-\xff][\\\\\\\\a-zA-Z0-9_\x7f-\xff]*$/\', $alias) > 0',     "function_alias(): '$alias' is not a valid function name");    $aliasNamespace = substr($alias, 0, strrpos($alias, '\\') !== false ? strrpos($alias, '\\') : 0);   $aliasName = substr($alias, strrpos($alias, '\\') !== false ? strrpos($alias, '\\') + 1 : 0);   $serializedOriginal = var_export($original, true);    eval("     namespace $aliasNamespace {       function $aliasName () {         return call_user_func_array($serializedOriginal, func_get_args());       }     }   ");  } 

In combination with namespace importer:

function import_namespace ($source, $destination) {    $args = func_get_args();   assert('count($args) == 2', 'import_namespace(): requires exactly two arguments');   assert('is_string($source) && is_string($destination)', 'import_namespace(): requires string arguments');    // valid function name - http://php.net/manual/en/functions.user-defined.php   assert('preg_match(\'/^([a-zA-Z_\x7f-\xff][\\\\\\\\a-zA-Z0-9_\x7f-\xff]*)?$/\', $source) > 0',     "import_namespace(): '$destination' is not a valid namespace name");   assert('preg_match(\'/^([a-zA-Z_\x7f-\xff][\\\\\\\\a-zA-Z0-9_\x7f-\xff]*)?$/\', $destination) > 0',     "import_namespace(): '$source' is not a valid namespace name");    foreach(get_declared_classes() as $class)     if (strpos($class, $source . '\\') === 0)       class_alias($class, $destination . ($destination ? '\\' : '') . substr($class, strlen($source . '\\')));    $functions = get_defined_functions();   foreach(array_merge($functions['internal'], $functions['user']) as $function)     if (strpos($function, $source . '\\') === 0)       function_alias($function, $destination . ($destination ? '\\' : '') . substr($function, strlen($source . '\\'))); } 
like image 39
luka8088 Avatar answered Oct 06 '22 16:10

luka8088