【问题标题】:Calling a PHP function defined in another namespace without the prefix调用在另一个命名空间中定义的 PHP 函数,不带前缀
【发布时间】:2011-03-22 10:45:13
【问题描述】:

在命名空间中定义函数时,

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

从另一个(或全局)命名空间调用时必须指定命名空间:

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

对于类,您可以使用“use”语句将类有效地导入当前命名空间[编辑:我认为您可以“使用 foo”来获取类,但显然不是。]

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

但这不适用于函数[并且考虑到函数的数量会很笨拙]:

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

你可以给命名空间起别名,使前缀更短,

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

但是有什么办法可以完全去掉前缀呢?

背景:我正在研究 Hamcrest 匹配库,它定义了许多工厂函数,其中许多被设计为嵌套。拥有命名空间前缀确实会破坏表达式的可读性。比较

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

use Hamcrest as h;
h\assertThat($names, 
    h\is(h\anArray(
        h\equalTo('Alice'), 
        h\startsWith('Bob'), 
        h\anything(), 
        h\hasLength(h\atLeast(12))
    )));

【问题讨论】:

    标签: php function namespaces


    【解决方案1】:

    PHP 5.6 将允许使用 use 关键字导入函数:

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

    有关详细信息,请参阅 RFC:https://wiki.php.net/rfc/use_function

    【讨论】:

    • 我刚刚在 windows 上构建了 5.6.0-dev 并试用了一下。似乎效果很好,尽管您必须单独导入每个函数。
    【解决方案2】:

    通过添加下面提到的 helper hack,您可以通过调用将 Hamcrest 命名空间中的所有内容导入到当前命名空间:

    import_namespace('Hamcrest', __NAMESPACE__);
    

    这里是 hack,function_alias 的工作方式类似于 http://www.php.net/manual/en/function.class-alias.php,除非适用于函数:

    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());
          }
        }
      ");
    
    }
    

    结合命名空间导入器:

    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 . '\\')));
    }
    

    【讨论】:

      【解决方案3】:

      我不知道一个优雅的解决方案,但是...

      您可以创建封装函数,将函数封装在外部命名空间中。这将使您保持代码的可读性...

      function assertThat($x, $y) { return h\assertThat($x, $y); }

      【讨论】:

      • 现有函数已经是调用真正静态工厂方法的便捷包装器。我可以提供没有命名空间的这个模块的副本,让用户决定他们想要导入哪个。效果是一样的,我敢打赌很容易实现自动化。
      猜你喜欢
      • 2017-01-22
      • 1970-01-01
      • 2016-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多