vue快速匹配路由_使用PHRoute快速进行PHP路由

vue快速匹配路由

PHRoute is an interesting package: it’s a fast regular expression based router that you can easily implement in small to medium projects. However, it’s not just very fast: there are filters, filter groups and named routes. You can also use a basic controllers system if things are getting bigger.

PHRoute是一个有趣的软件包:它是一个基于正则表达式的快速路由器,您可以轻松地在中小型项目中实现。 但是,它不仅速度很快:还有过滤器,过滤器组和命名路由。 如果事情变得越来越大,您还可以使用基本的控制器系统。

That said, today we will see how to use it and how to implement its features in a sample project. Also, we are going to see what’s under the hood: PHRoute is a result of many experiments and tests by different people.

也就是说,今天我们将在示例项目中看到如何使用它以及如何实现其功能。 此外,我们将了解幕后情况:PHRoute是不同人员进行的许多实验和测试的结果。

Let’s start by installing it!

让我们从安装开始!

安装 (Install)

You can add PHRoute to your project with Composer in seconds. Just add this line to your composer.json file:

您可以在几秒钟内使用Composer将PHRoute添加到项目中。 只需将此行添加到您的composer.json文件:

{ 
        "require": 
        { 
            "phroute/phroute": "1.*" 
        } 
    }

Type the composer install command and you’re in. Now, let’s move on to our test project.

输入composer install命令,您就可以进入了。现在,让我们继续进行测试项目。

示例项目和第一个示例 (Sample Project and First Example)

For a better understanding of every concept of PHRoute, it is a good idea to have a sample project to work with. Today we are going to make a basic API for a books database service.

为了更好地理解PHRoute的每个概念,建议您使用一个示例项目。 今天,我们将为图书数据库服务创建一个基本的API。

Here’s the database scheme we are going to use:

这是我们将要使用的数据库方案:

The Database Scheme

If you want to do some tests, this is the SQL schema dump I used (with some extra dummy data).

如果要进行一些测试,这是我使用SQL模式转储(带有一些额外的虚拟数据)。

CREATE TABLE IF NOT EXISTS authors (id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(250) NOT NULL, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3;

    INSERT INTO authors (id, name) 
    VALUES 
    (1, 'Dan Brown'), 
    (2, 'Paulo Coelho');

    CREATE TABLE IF NOT EXISTS categories (id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(250) NOT NULL, PRIMARY KEY (id)) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3;

    INSERT INTO categories (id, name) 
    VALUES 
    (1, 'Thriller'), 
    (2, 'Novel');

    CREATE TABLE IF NOT EXISTS books (id int(10) unsigned NOT NULL AUTO_INCREMENT,   title varchar(250) NOT NULL, isbn varchar(50) NOT NULL, year int(11) NOT NULL,   pages int(11) NOT NULL, author_id int(10) unsigned NOT NULL, category_id int(10) unsigned NOT NULL, PRIMARY KEY (id), KEY author_id (author_id,category_id), KEY category_id (category_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7;

    INSERT INTO books (id, title, isbn, year, pages, author_id, category_id) 
    VALUES 
    (1, 'The Zahir', '0-06-083281-9', 2005, 336, 2, 2), 
    (2, 'The Devil and Miss Prym', '0-00-711605-5', 2000, 205, 2, 2), 
    (3, 'The Alchemist', '0-06-250217-4', 1988, 163, 2, 2), 
    (4, 'Inferno', '978-0-385-53785-8', 2013, 480, 1, 1), 
    (5, 'The Da Vinci Code', '0-385-50420-9', 2003, 454, 1, 1), 
    (6, 'Angels & Demons', '0-671-02735-2', 2000, 616, 1, 1);

We are not going to write anything really complex. Actually, writing some routes to emulate an API request in a very basic way will be enough. If you want to write a real world API there are many concepts you have to know, but today we are just taking a look at PHRoute.

我们不会写任何真正复杂的东西。 实际上,以一种非常基本的方式编写一些路由来模拟API请求就足够了。 如果您想编写一个现实世界的API,则必须了解许多概念 ,但是今天我们仅看一下PHRoute。

Before we start with specific routes, let’s analyze the main application structure. This is what we are going to put in our index.php file.

在开始特定的路由之前,让我们分析主要的应用程序结构。 这就是我们要放入index.php文件中的内容。

<?php

    require 'vendor/autoload.php';

    function processInput($uri){        
        $uri = implode('/', 
            array_slice(
                explode('/', $_SERVER['REQUEST_URI']), 3));         

            return $uri;    
    }

    function processOutput($response){
        echo json_encode($response);    
    }

    function getPDOInstance(){
        return new PDO('mysql:host=localhost;dbname=booksapi;charset=utf8', 'root', '');
    }

    $router = new Phroute\RouteCollector(new Phroute\RouteParser);

    $router->get('hello', function(){ 
        return 'Hello, PHRoute!';   
    });

    $dispatcher = new Phroute\Dispatcher(router);

    try {

        $response = $dispatcher->dispatch($_SERVER['REQUEST_METHOD'], processInput($_SERVER['REQUEST_URI']));

    } catch (Phroute\Exception\HttpRouteNotFoundException $e) {

        var_dump($e);      
        die();

    } catch (Phroute\Exception\HttpMethodNotAllowedException $e) {

        var_dump($e);       
        die();

    }

    processOutput($response);

We have three utility methods: processInput, processOutput and getPDOInstance. We will use the first two to be sure we are getting the right input and the right output. The third will prepare the necessary PDO instance.

我们有三种实用方法: processInputprocessOutputgetPDOInstance 。 我们将使用前两个来确保获得正确的输入和正确的输出。 第三个将准备必要的PDO实例。

Note: the second parameter of the array_slice method is “3” because of my personal specific project setup. Change it as your base url changes.

注意:由于我个人特定的项目设置, array_slice方法的第二个参数是“ 3”。 随着您的基本网址更改,对其进行更改。

After that, we declare our routes using the object $router, instance of the RouteController class. Then, the magic happens in the $dispatcher->dispatch() method, that takes two parameters: the $_SERVER request method (GET, POST etc.) and the specific request uri. With this information, the dispatcher calls the right route and executes the code in the closure. The return value is stored in the $response variable, that is given to the method processOutput() that echoes it as a JSON string.

之后,我们使用对象$router声明路由, RouteController类的实例。 然后,魔术发生在$dispatcher->dispatch()方法中,该方法具有两个参数: $_SERVER请求方法(GET,POST等)和特定的请求uri。 有了此信息,调度程序将调用正确的路由并在闭包中执行代码。 返回值存储在$response变量中,该变量被提供给processOutput()方法,该方法将其作为JSON字符串回显。

As you can see, in this specific example we declared a single route: hello.

如您所见,在此特定示例中,我们声明了一条路由: hello

Note: If you want, however, you can enhance the actual structure. Create a new file and call it routes.php. Then, include it from the main index.php file right after the $router object initialization: you will have all your routes in a separate file. A more elegant solution, in my opinion.

注意:但是,如果需要,可以增强实际结构。 创建一个新文件,并将其命名为routes.php 。 然后,在$router对象初始化之后,立即从主index.php文件中包含它:您将所有路由都放在单独的文件中。 我认为,这是一种更优雅的解决方案。

That said, you now know everything you need about the basic structure of our example.

就是说,您现在知道了有关示例基本结构的所有信息。

Let’s make our first routes!

让我们开始我们的第一条路线!

路线 (Routes)

简单的路线 (A simple route)

Ok, let’s see what we can do with routes and how much we can customize them for our needs.

好的,让我们看看我们可以使用路线做什么,以及我们可以为自己的需求定制多少。

We are starting with the simplest thing: the authors list.

我们从最简单的事情开始:作者列表。

$router->get('authors', function(){      
        $db = getPDOInstance();

        $sql = 'SELECT * FROM authors;';  
        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));

        $st->execute();

        $result = $st->fetchAll(PDO::FETCH_CLASS);
	    
        return $result;  
    });

In the first line we declare our route name, authors.

在第一行中,我们声明路线名称authors

Let’s test the route: this is the result.

让我们测试一下路线:这就是结果。

[{"id":"1","name":"Dan Brown"},{"id":"2","name":"Paulo Coelho"}]

Great!

大!

添加参数 (Adding a parameter)

Now we can make a step forward: what about adding a parameter, to get a single author’s details, given the id?

现在我们可以向前迈出一步:在给定ID的情况下,如何添加参数以获取单个作者的详细信息?

Something like that:

像这样:

$router->get('author/{id}', function($id){      
        $db = getPDOInstance(); 

        $sql = 'SELECT * FROM `authors` WHERE `id` = :id';     
    
        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        $st->execute(array(':id' => $id));

        $result = $st->fetchAll(PDO::FETCH_CLASS);
 
        return $result;
    });

You can pass a parameter using a {variable_name} placeholder, with the same chosen name as a parameter for the closure. In this example, we have a {id} placeholder corresponding to the $id parameter. You can specify any parameter you want: no limits.

您可以使用{variable_name}占位符传递参数,并使用与闭合参数相同的名称。 在此示例中,我们有一个与$id参数对应的{id}占位符。 您可以指定任何参数:无限制。

Sometimes a parameter can be optional. Let’s make another example: if we use the books URL we want to retrieve a list of all the database books. But, if we specify an id like books/1 we will get the books list of the given category.

有时参数可以是可选的。 让我们再举一个例子:如果我们使用books URL,我们想检索所有数据库书的列表。 但是,如果我们指定一个类似于books/1的ID,我们将获得给定类别的图书清单。

Here we go:

开始了:

$router->get('books/{category_id}?', function($category_id = null){         
        $db = getPDOInstance();
         
        if($category_id == null)
        {
    	    $sql = 'SELECT * FROM `books`;';
    	    $params = array();
        }
        else
        {
    	    $sql = 'SELECT * FROM `books` WHERE `category_id` = :category_id;';
            $params = array(':category_id' => $category_id);
        }

        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        $st->execute($params);

        $result = $st->fetchAll(PDO::FETCH_CLASS);
 
        return $result;   
    });

Adding a “?” after the parameter placeholder means that it will be optional. Of course, it’s a good idea to specify a default value in the closure declaration.

添加一个“?” 在参数占位符之后表示它是可选的。 当然,在闭包声明中指定默认值是一个好主意。

使用不同的动词 (Using different verbs)

Until now we created only GET routes. What about other HTTP verbs?

到目前为止,我们仅创建了GET路线。 那么其他HTTP动词呢?

No problem. Take a look here:

没问题。 在这里看看:

$router->get($route, $handler);    // used for GET-only requests
$router->post($route, $handler);   // used for POST-only requests
$router->delete($route, $handler); // used for DELETE-only requests
$router->any($route, $handler);    // used for all verbs

Let’s make an example POST route. It’s time to add a new book to our collection!

让我们做一个示例POST路由。 现在是时候将新书添加到我们的收藏中了!

$router->post('book', function(){       
        $db = getPDOInstance();
        $bookData = $_POST;

        $sql = 'INSERT INTO table_name (id, title, isbn, year, pages, author_id, category_id) VALUES (NULL, :title, :isbn, :year, :pages, :author_id, :category_id);';

        $params = array(
    	    ':title' => 'The Winner Stands Alone',
    	    ':isbn' => '978-88-452-6279-1',
    	    ':year' => 2009,
    	    ':pages' => 361,
    	    ':author_id' => 2,
    	    ':category_id' => 2
        );

        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        $result = $st->exec($params);

        if($result)
        {
    	    return $db->lastInsertId();
        }
        else
        {
    	    return false;
        }
    });

Let’s imagine that we have a form to fill with book data: its action attribute will point to the book route we created right now!

假设我们有一个表单来填充书籍数据:它的action属性将指向我们当前创建的book路线!

Now we are going to take another step forward: it’s time to “protect” our routes!

现在,我们将迈出新的一步:是时候“保护”我们的路线了!

筛选器 (Filters)

Actually, everyone who enters the book POST route can insert a new book in our collection. That’s cool, but this is not like things go usually. What if we want to protect our routes? Filters are what we need.

其实,大家谁进入book POST路线可以将我们的集合中的一本新书。 太酷了,但这与通常情况不同。 如果我们要保护路线怎么办? 过滤器是我们所需要的。

Filters are very similar to routes: they have a name and an associated closure, executed when the filter is called somewhere.

过滤器与路由非常相似:它们具有名称和关联的闭包,当在某处调用过滤器时执行。

So, what’s the difference? A filter can be easily called before (or after) a route.

那么,有什么区别呢? 可以在路由之前(或之后)轻松调用过滤器。

过滤 (Filter)

Let’s make an example:

让我们举个例子:

$router->filter('logged_in', function(){    
        if(!$_SESSION['user_id']){
            header('Location: /login');
            return false;
        }
    });

    $router->post('book', function(){       
        $db = getPDOInstance();
        $bookData = $_POST;

        $sql = 'INSERT INTO table_name (id, title, isbn, year, pages, author_id, category_id) VALUES (NULL, :title, :isbn, :year, :pages, :author_id, :category_id);';

        $params = array(
    	    ':title' => 'The Winner Stands Alone',
    	    ':isbn' => '978-88-452-6279-1',
    	    ':year' => 2009,
    	    ':pages' => 361,
    	    ':author_id' => 2,
    	    ':category_id' => 2
        );

        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        $result = $st->exec($params);

        if($result)
        {
    	    return $db->lastInsertId();
        }
        else
        {
    	    return false;
        }
    }, array('before' => 'logged_in'));

First of all, we declared the filter with the filter() method of the $router object. The syntax is the same of as with a route. We are giving it a name and a closure that will be executed at the right time.

首先,我们使用$router对象的filter()方法声明了过滤filter() 。 语法与路由相同。 我们给它一个名称和一个将在适当的时间执行的闭包。

Ok, but what is the “right time”?

好的,但是什么是“正确的时间”?

We are deciding on it now: we just added a third parameter to the post() method. This third parameter is an array, where we specify the key before with the name of the filter (logged_in). From this moment, before every single call to the book post route, the logged_in filter (and executed its closure content) will be also called.

我们现在就决定:我们只是在post()方法中添加了第三个参数。 第三个参数是一个数组,我们before其中使用过滤器的名称( logged_in )指定键。 从此刻开始,在每次调用book帖路径之前,还将调用logged_in过滤器(并执行其关闭内容)。

In this specific case, we are checking for a session user_id variable to see if the user is logged in.

在这种情况下,我们将检查会话user_id变量以查看用户是否已登录。

There is also the after key that is used to run a filter right after the route call. Here’s an example.

还有一个after键,用于在路由调用之后立即运行过滤器。 这是一个例子。

$router->filter('clean', function(){    
        // cleaning code after the route call...
    });

    $router->post('book', function(){       
        $db = getPDOInstance();
        $bookData = $_POST;

        $sql = 'INSERT INTO table_name (id, title, isbn, year, pages, author_id, category_id) VALUES (NULL, :title, :isbn, :year, :pages, :author_id, :category_id);';

        $params = array(
    	    ':title' => 'The Winner Stands Alone',
    	    ':isbn' => '978-88-452-6279-1',
    	    ':year' => 2009,
    	    ':pages' => 361,
    	    ':author_id' => 2,
    	    ':category_id' => 2
        );

        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        $result = $st->exec($params);

        if($result)
        {
    	    return $db->lastInsertId();
        }
        else
        {
    	    return false;
        }
    }, array('after' => 'clean'));

If you need, you can also specify more than one filter at the same time. All you have to do is use an array of strings instead of a single string.

如果需要,还可以同时指定多个过滤器。 您要做的就是使用字符串数组而不是单个字符串。

$router->filter('filter1', function(){    
        // filter 1 operations...
    });

    $router->filter('filter2', function(){    
        // filter 2 operations...
    });

    $router->post('book', function(){       
        $db = getPDOInstance();
        $bookData = $_POST;

        $sql = 'INSERT INTO table_name (id, title, isbn, year, pages, author_id, category_id) VALUES (NULL, :title, :isbn, :year, :pages, :author_id, :category_id);';

        $params = array(
    	    ':title' => 'The Winner Stands Alone',
    	    ':isbn' => '978-88-452-6279-1',
    	    ':year' => 2009,
    	    ':pages' => 361,
    	    ':author_id' => 2,
    	    ':category_id' => 2
        );

        $st = $db->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
        $result = $st->exec($params);

        if($result)
        {
    	    return $db->lastInsertId();
        }
        else
        {
    	    return false;
        }
    }, array('after' => array('filter1', 'filter2')));

筛选器组 (Filter Groups)

Let’s imagine a real world case: let’s say we have three post routes, one for every entity (author, book, category). It would be boring to add the logged_in filter three different times.

让我们想象一个现实世界的情况:假设我们有三种发布途径,每个实体(作者,书籍,类别)一个。 在三个不同的时间添加logged_in过滤器会很无聊。

Don’t worry: filter groups are here to help.

不用担心:过滤器组可为您提供帮助。

$router->filter('logged_in', function(){    
    if(!isset($_SESSION['user_id'])) 
    {
        header('Location: /login');
        return false;
    }
});

$router->group(array('before' => 'logged_in'), function($router){

    $router->post('book', function(){
        // book insert code...
    });

    $router->post('author', function(){
        // author insert code...
    });

    $router->post('category', function(){
        // category insert code...
    });

});

With this single group, we defined the same filter for three different routes.

在这单个组中,我们为三个不同的路线定义了相同的过滤器。

Note: If you need, you can also nest groups in other groups as many times as you like.

注意:如果需要,您还可以根据需要将组嵌套在其他组中多次。

越来越多的项目? 是时候使用控制器了! (Growing project? Time to use Controllers!)

Our project is growing up and organizing our code base in a single file is really heavy, and sloppy. What about using controllers?

我们的项目正在成长,并且在一个文件中组织代码库确实很繁琐。 那使用控制器呢?

Yes: PHRoute is not just about routes. When things go wild it’s time to organize them.

是的:PHRoute不仅与路线有关。 当事情变得疯狂时,就该组织起来了。

First of all, let’s see what the structure of a controller is like. Take a look at this example (we can put it in our routes.php file):

首先,让我们看看控制器的结构是什么样的。 看一下这个例子(我们可以把它放在我们的routes.php文件中):

<?php

    class Author {
        public function getIndex()
        {
            // get author list data here...
            return $result;
        }
        
        public function postAdd()
        {           
            // add a new author to the database
            return $insertId;
        } 
    }
    

    $router->controller('author', 'Author');

We created an Author class. In this class we put two methods: getIndex() and postAdd().

我们创建了一个Author类。 在此类中,我们放置了两个方法: getIndex()postAdd()

Then, with the controller() method of the $router object, we link the author url to the Author class. So, if we enter the URL author in our browser the getIndex() method will be automatically called. Same goes for the postAdd() method, that will be bound to the author/add (POST) url.

然后,使用$router对象的controller()方法,将author url链接到Author类。 因此,如果我们在浏览器中输入URL author ,则会自动调用getIndex()方法。 postAdd()方法也是如此,该方法将绑定到author/add (POST)网址。

This auto resolving name feature is quite interesting, but actually not enough.

这种自动解析名称功能非常有趣,但实际上还不够。

The controller part is at an early stage of development and needs many improvements. One of them is the possibility to define parameters for controller methods. Or, maybe, an easy way to define filters for some methods of a controller (and not “all or nothing”).

控制器部分尚处于开发的早期阶段,需要进行许多改进。 其中之一是可以为控制器方法定义参数。 或者,也许是一种为控制器的某些方法(而不是“全有或全无”)定义过滤器的简便方法。

结论 (Conclusion)

There is a lot of work to do, especially on the controllers side. As a developer, I think it would be great to have a generic basic controller class to handle all the dirty work (with filters, methods parameters and so on). There’s also a lack of documentation.

有很多工作要做,尤其是在控制器方面。 作为开发人员,我认为最好有一个通用的基本控制器类来处理所有脏工作(包括过滤器,方法参数等)。 也缺少文档。

On the other hand, PHRoute comes with a really fast router. On the GitHub page of the project you can see some stats about a comparison with Laravel’s core router: the results are amazing. In the worst case scenario PHRoute is about forty (yes, 40) times faster.

另一方面,PHRoute带有一个非常快的路由器。 在该项目的GitHub页面上,您可以看到有关与Laravel核心路由器进行比较的一些统计信息:结果令人惊讶。 在最坏的情况下,PHRoute快40倍(是40倍)。

If you want to know specific details about the “engine” behind this router, you can visit the nikic page on GitHub where he explained everyhing, with tests, benchmarks and related results.

如果您想了解有关此路由器后面的“引擎”的特定详细信息,可以访问GitHub上nikic页面,他在其中解释了所有内容 ,包括测试,基准测试和相关结果。

Are you going to try PHRoute? Let me know what you think about it!

您要尝试PHRoute吗? 让我知道您的想法!

翻译自: https://www.sitepoint.com/fast-php-routing-phroute/

vue快速匹配路由

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值