Sahte yöntem, PHP niteliklerini?

5 Cevap php

O. NET yöntem PHP niteliklerini, ya da bir şekilde bu taklit için eşdeğer kullanmak mümkün mü?

Context

Biz bir sürü gibi bir in-house URL yönlendirme sınıfı var. Bugün çalıştığını yolu biz ilk öylesine gibi merkezi bir güzergah yöneticisi ile tüm yolları, kayıt olması:

$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));

Bir rota karşılaştı zaman, geri çağırma yöntemi (durumlarda yukarıda statik sınıf yöntemleri) denir. Bununla birlikte, bu en azından kodu, yönteminden yol ayırır.

Ben C # yapmış olabilir gibi, yakın yöntemine yol koymak için bazı yöntem için arıyorum:

<Route Path="admin/test/">
public static void SomeMethod() { /* implementation */ }

Benim seçenekleri onları şimdi gördüğünüz gibi, böyle bir şey için bana izin verir Phpdoc uzatma çeşit oluşturmak için ya vardır:

/**
 * @route admin/test/
 */
public static function SomeMethod() { /* implementation */ }

Ama bu PHPDoc için bir ayrıştırıcı yeniden / yazma gerektirir, ve büyük olasılıkla oldukça yavaş olacaktır.

Diğer seçenek kendi sınıfa her rotayı ayırmak, ve aşağıdaki gibi bir yöntemleri var olacaktır:

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

Ancak, bu hala her sınıf kayıt gerektirir, ve bu gibi sınıfların büyük bir sayıda (ekstra kod miktarını söz değil) olacaktır.

Yani benim seçenekleri burada nelerdir? Ne çağırır yöntemine yakın rotayı tutmak için en iyi yol olacağını?

5 Cevap

Bu benim bu çözme sona erdi nasıl. article provided by Kevin büyük bir yardımcı oldu. ReflectionClass ve ReflectionMethod :: getDocComment kullanarak, ben çok kolay Phpdoc yorumlarla yürüyerek gezebilirsiniz. Küçük bir düzenli ifade herhangi bir @route bulur ve yöntemine kayıtlı.

Yansıma biz yollarının bir sürü var çünkü (bizim durumumuzda, ayrı bir işlevi RegiserRoute kodlanmış aramaları sahip olarak yavaş yaklaşık 2,5 kat), ve, biz yollarının bitmiş listesini önbelleğe zorunda olduğunu hızlı değil Memcached, böylece yansıma her sayfa yük gereksizdir. Toplamda ortalama her sayfası yükleme kullanılan 18MS üzerine (yansıma önbelleğe zaman ortalama 1,7 ms yolları kayıt 7ms alarak gidiş sona erdi.

Aşağıdaki gibi manuel kayıt gerekiyorsa bir alt sınıfta geçersiz olabilir bunu kodudur:

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

Bana doğru yönde işaret için herkese teşekkürler, burada iyi öneriler bir sürü vardı! Biz bize onu çağırır koduna yakın rota tutmanızı sağlar çünkü bu yaklaşım ile gitti:

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;
    }
}

PHP 5.3 kullanarak, yönlendirmek için kod bağlamak için closures veya "Anonymous functions" kullanabilirsiniz.

Örneğin:

<?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!
?>

İşte sizin ihtiyaçlarınıza uygun bir yöntem. Yolları içeren Her sınıf bir arabirim uygulamak ve yollarının bir listesini toplamak için bu arabirimi uygulayan tüm tanımlı sınıfları ile daha sonra döngü gerekir. Arayüz UrlRoute nesneleri bir dizi iade edilmesini beklediği tek bir yöntemi içerir. Bunlar daha sonra varolan URL yönlendirme sınıfını kullanarak kayıtlıdır.

Düzenleme: Ben sadece UrlRoute sınıf muhtemelen de SınıfAdı için bir alan içermelidir, düşünüyordum. Sonra $oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method)) $oRouteManager->RegisterRoute($urlRoute) basitleştirilmiş olabilir. Ancak, bu mevcut çerçevede bir değişiklik gerektirecektir ...

interface IUrlRoute
{
    public static function GetRoutes();
}

class UrlRoute
{
    var $route;
    var $method;

    public function __construct($route, $method)
    {
    	$this->route = $route;
    	$this->method = $method;
    }
}

class Page1 implements IUrlRoute
{
    public static function GetRoutes()
    {
    	return array(
    		new UrlRoute('page1/test/', 'test')
    	);
    }

    public function test()
    {
    }
}

class Page2 implements IUrlRoute
{
    public static function GetRoutes()
    {
    	return array(
    		new UrlRoute('page2/someroute/', 'test3'),
    		new UrlRoute('page2/anotherpage/', 'anotherpage')
    	);
    }

    public function test3()
    {
    }

    public function anotherpage()
    {
    }
}

$classes = get_declared_classes();
foreach($classes as $className)
{
    $c = new ReflectionClass($className);
    if( $c->implementsInterface('IUrlRoute') )
    {
    	$fnRoute = $c->getMethod('GetRoutes');
    	$listRoutes = $fnRoute->invoke(null);

    	foreach($listRoutes as $urlRoute)
    	{
    		$oRouteManager->RegisterRoute($urlRoute->route, array($className, $urlRoute->method));	
    	}
    }
}

Ben anında yolları kayıt arayüzleri bir arada ve bir singleton sınıfı kullanmak istiyorum.

Ben böylece FirstRouter, SecondRouter ve benzeri yönlendirici sınıfları bir adlandırma kuralı kullanmak istiyorum. Bu çalışma, bu sağlayacak:

foreach (get_declared_classes() as $class) {
    if (preg_match('/Router$/',$class)) {
	new $class;
    }
}

O benim yönlendirici yöneticisi ile tüm bildirilen sınıfları kayıt olur.

Bu rota yöntemi çağırmak için kod

$rm = routemgr::getInstance()->route('test/test');

Bir yönlendirici yöntemi bu gibi görünecektir

static public function testRoute() {
if (self::$register) {
    return 'test/test'; // path
}
echo "testRoute\n";
}

Arayüzleri

interface getroutes {
    public function getRoutes();
}

interface router extends getroutes {
    public function route($path);
    public function match($path);
}

interface routes {
    public function getPath();
    public function getMethod();
}

Ve bu benim tanımı bir rota av olduğunu

class route implements routes {
    public function getPath() {
    return $this->path;
    }
    public function setPath($path) {
    $this->path = $path;
    }
    public function getMethod() {
    return $this->method;
    }
    public function setMethod($class,$method) {
    $this->method = array($class,$method);
    return $this;
    }
    public function __construct($path,$method) {
    $this->path = $path;
    $this->method = $method;
    }
}

Router yönetici

class routemgr implements router {
    private $routes;
    static private $instance;
    private function __construct() {
    }
    static public function getInstance() {
    if (!(self::$instance instanceof routemgr)) {
        self::$instance = new routemgr();
    }
    return self::$instance;
    }
    public function addRoute($object) {
    $this->routes[] = $object;
    }
    public function route($path) {
    foreach ($this->routes as $router) {
        if ($router->match($path)) {
    	$router->route($path);
        }
    }
    }
    public function match($path) {
    foreach ($this->routes as $router) {
        if ($router->match($path)) {
    	return true;
        }
    }
    }
    public function getRoutes() {
    foreach ($this->routes as $router) {
        foreach ($router->getRoutes() as $route) {
    	$total[] = $route;
        }
    }
    return $total;
    }
}

Ve kendini süper sınıf kayıt

class selfregister implements router {
    private $routes;
    static protected $register = true;
    public function getRoutes() {
    return $this->routes;
    }
    public function __construct() {
    self::$register = true;
    foreach (get_class_methods(get_class($this)) as $name) {
        if (preg_match('/Route$/',$name)) {
    	$path = call_user_method($name, $this);
    	if ($path) {
    	    $this->routes[] = new route($path,array(get_class($this),$name));
    	}
        }
    }
    self::$register = false;
    routemgr::getInstance()->addRoute($this);
    }
    public function route($path) {
    foreach ($this->routes as $route) {
        if ($route->getPath() == $path) {
    	call_user_func($route->getMethod());
        }
    }
    }
    public function match($path) {
    foreach ($this->routes as $route) {
        if ($route->getPath() == $path) {
    	return true;
        }
    }
    }
}

Ve nihayet kendini yönlendirici sınıfı kayıt

class aRouter extends selfregister {
    static public function testRoute() {
    if (self::$register) {
        return 'test/test';
    }
    echo "testRoute\n";
    }
    static public function test2Route() {
    if (self::$register) {
        return 'test2/test';
    }
    echo "test2Route\n";
    }
}

Eğer fonksiyon tanımı (IMHO) için yol koyabilirsiniz yakın sağ sınıf tanımı öncedir. böylece olurdu

$oRouteManager->RegisterRoute('test/', array('CTest', 'SomeMethod'));
class CTest {
    public static function SomeMethod() {}
}

ve

$oRouteManager->RegisterRoute('admin/test/', array('CAdmin', 'SomeMethod'));
$oRouteManager->RegisterRoute('admin/foo/', array('CAdmin', 'SomeOtherMethod'));
class CAdmin {
    public static function SomeMethod() {}
    public static function SomeOtherMethod() {}
}