PHP伪造方法属性?

Veg*_*sen 12 php methods attributes url-routing decorator

是否可以在PHP中使用.NET方法属性的等价物,或以某种方式模拟这些?

上下文

我们有一个我们非常喜欢的内部URL路由类.它今天的工作方式是我们首先必须使用中央路由管理器注册所有路由,如下所示:

$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
Run Code Online (Sandbox Code Playgroud)

无论何时遇到路由,都会调用回调方法(在上面的情况下,它们是静态类方法).但是,这会将路径与方法分开,至少在代码中是这样.

我正在寻找一些方法来使路线更接近方法,就像你在C#中所做的那样:

<Route Path="admin/test/">
public static void SomeMethod() { /* implementation */ }
Run Code Online (Sandbox Code Playgroud)

我现在看到的选项是要么创建某种phpDoc扩展,允许我这样的东西:

/**
 * @route admin/test/
 */
public static function SomeMethod() { /* implementation */ }
Run Code Online (Sandbox Code Playgroud)

但这需要为phpDoc编写/重用解析器,并且很可能会很慢.

另一种选择是将每个路由分成它自己的类,并使用如下方法:

class CAdminTest extends CRoute
{
    public static function Invoke() { /* implementation */ }
    public static function GetRoute() { return "admin/test/"; }
}
Run Code Online (Sandbox Code Playgroud)

但是,这仍然需要注册每个类,并且会有很多这样的类(更不用说额外的代码量).

那么我的选择是什么?保持路线接近它调用的方法的最佳方法是什么?

Veg*_*sen 10

这就是我最终解决这个问题的方法.凯文提供文章是一个巨大的帮助.通过使用ReflectionClass和ReflectionMethod :: getDocComment,我可以非常轻松地浏览phpDoc注释.一个小的正则表达式找到任何@route,并注册到该方法.

反射不是那么快(在我们的例子中,在单独的函数中对RegiserRoute进行硬编码调用的速度大约是2.5倍),并且因为我们有很多路由,所以我们必须缓存完成的路由列表Memcached,因此每次页面加载都不需要反射.总的来说,当缓存时,我们最终从7ms开始将路由注册到1,7ms(每页加载的反射平均使用18ms).

执行此操作的代码,如果需要手动注册,可以在子类中重写,如下所示:

public static function RegisterRoutes()
{
    $sClass = get_called_class(); // unavailable in PHP < 5.3.0
    $rflClass = new ReflectionClass($sClass);
    foreach ($rflClass->getMethods() as $rflMethod)
    {
        $sComment = $rflMethod->getDocComment();
        if (preg_match_all('%^\s*\*\s*@route\s+(?P<route>/?(?:[a-z0-9]+/?)+)\s*$%im', $sComment, $result, PREG_PATTERN_ORDER)) 
        {
            foreach ($result[1] as $sRoute)
            {
                $sMethod = $rflMethod->GetName();
                $oRouteManager->RegisterRoute($sRoute, array($sClass, $sMethod));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

感谢大家指点我正确的方向,这里有很多好建议!我们采用这种方法只是因为它允许我们保持路由接近它调用的代码:

class CSomeRoutable extends CRoutable
{
    /**
     * @route /foo/bar
     * @route /for/baz
     */
    public static function SomeRoute($SomeUnsafeParameter)
    {
        // this is accessible through two different routes
        echo (int)$SomeUnsafeParameter;
    }
}
Run Code Online (Sandbox Code Playgroud)


Atl*_*tli 5

使用PHP 5.3,您可以使用闭包"匿名函数"将代码绑定到路由.

例如:

<?php
class Router
{
    protected $routes;
    public function __construct(){
        $this->routes = array();
    }

    public function RegisterRoute($route, $callback) {
       $this->routes[$route] = $callback;
    }

    public function CallRoute($route)
    {
        if(array_key_exists($route, $this->routes)) {
            $this->routes[$route]();
        }
    }
}


$router = new Router();

$router->RegisterRoute('admin/test/', function() {
    echo "Somebody called the Admin Test thingie!";
});

$router->CallRoute('admin/test/');
// Outputs: Somebody called the Admin Test thingie!
?>
Run Code Online (Sandbox Code Playgroud)