您的位置 首页 php

使用 PHRoute 的快速 PHP 路由

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

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

让我们从安装它开始吧!

安装

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

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

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

示例项目和第一个示例

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

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

如果你想做一些测试,这是我使用的 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);  

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

在开始具体路由之前,我们先来分析一下主要的应用结构。这就是我们要放入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 get PDO Instance(){
        return new PDO('mysql:host=localhost;dbname=booksapi;charset=utf8', ' root ', '');
    }

    $router = new Phroute\ Route Collector(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);  

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

注意: 方法的第二个参数array_slice是“3”,因为我个人具体的项目设置。随着您的基本网址更改,请更改它。

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

如您所见,在这个特定示例中,我们声明了一条路线:hello.

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

也就是说,您现在已经了解了我们示例的基本结构所需的一切。

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

路线

一条简单的路线

好的,让我们看看我们可以用路由做什么,以及我们可以根据我们的需要定制多少。

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

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

在第一行中,我们声明了我们的路由名称,authors.

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

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

伟大的!

添加参数

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

像这样的东西:

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

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

有时参数可以是可选的。让我们再举一个例子:如果我们使用booksURL,我们想要检索所有数据库书籍的列表。但是,如果我们指定一个像这样的 id,books/1我们将获得给定类别的书籍列表。

开始了:

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

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

使用不同的动词

到目前为止,我们只创建了 GET 路由。其他 HTTP 动词呢?

没问题。看看这里:

 $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  

让我们做一个示例 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;
        }
    });  

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

现在我们要向前迈出一步:是时候“保护”我们的路线了!

过滤器

实际上,每个进入bookPOST 路径的人都可以在我们的收藏中插入一本新书。这很酷,但这与通常情况不同。如果我们想保护我们的路线怎么办?过滤器是我们需要的。

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

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

筛选

让我们举个例子:

 $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'));  

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

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

我们现在正在决定:我们只是在post()方法中添加了第三个参数。第三个参数是一个数组,我们在其中指定before带有过滤器名称的键 ( logged_in)。从这一刻起,在每次调用bookpost 路由之前,logged_in过滤器(并执行其闭包内容)也将被调用。

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

还有一个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'));  

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

 $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')));  

过滤组

让我们想象一个真实的案例:假设我们有三个发布路线,每个实体(作者、书籍、类别)都有一个。logged_in三次添加过滤器会很无聊。

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

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

});  

对于这个单一的组,我们为三个不同的路由定义了相同的过滤器。

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

成长项目?是时候使用控制器了!

我们的项目正在成长,将我们的代码库组织在一个文件中确实很繁重,而且很草率。使用控制器怎么样?

是的:PHRoute 不仅仅是关于路线。当事情变得疯狂时,是时候组织它们了。

首先,让我们看看控制器的结构是什么样的。看一下这个例子(我们可以把它放在我们的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');  

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

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

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

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

结论

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

另一方面,PHRoute 带有一个非常快的路由器。在项目的 GitHub 页面上,你可以看到一些与 Laravel 核心路由器进行比较的统计数据:结果令人惊叹。在最坏的情况下,PHRoute 大约快四十(是的,四十)倍。

文章来源:智云一二三科技

文章标题:使用 PHRoute 的快速 PHP 路由

文章地址:https://www.zhihuclub.com/77011.shtml

关于作者: 智云科技

热门文章

网站地图