代码之家  ›  专栏  ›  技术社区  ›  David Harkness

调用另一个命名空间中定义的不带前缀的PHP函数

  •  29
  • David Harkness  · 技术社区  · 14 年前

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

    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))
        )));
    
    3 回复  |  直到 11 年前
        1
  •  35
  •   David Harkness    11 年前

    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

        2
  •  7
  •   luka8088    11 年前

    通过添加下面提到的助手黑客,您可以通过调用将所有内容从hamcrest名称空间导入到当前名称空间:

    import_namespace('Hamcrest', __NAMESPACE__);
    

    以下是黑客,函数别名 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
  •  1
  •   Borealid    14 年前

    我不知道 优雅的 解决方案,但…

    可以创建封装外部命名空间中的函数的包装函数。这样可以保持代码的可读性…

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