个性化阅读
专注于IT技术分析

如何在Symfony 3中基于数字值(YouTube或URL Shortener样式)创建非顺序唯一ID

如果你的数据库中有一些表, 其主键是一个数字, 通常在Symfony中带有CRUD视图, 则你可以在浏览器中看到此寄存器, 并带有某种URL, 该URL在URL中显示ID, 例如http:// project /用户/ 12。为了使这一点易于理解, 让我们假设你有一些没有保护的Web系统(没有任何类型的用户系统或角色保护)。在此系统中, 你可以创建个人资料并保存其他人看不到的信息。你的个人资料可通过以下网址访问:

http://imaginarysystem.com/profiles/2

在这种情况下, 你的个人资料由数字2标识(应用程序用户表中的ID为2), 只需访问该URL, 即可访问你的信息。现在, 如果将配置文件的编号从2更改为123, 该怎么办:

http://imaginarysystem.com/profiles/123

这样就可以看到用户号123的信息!这一点都不好, 因为在我们的系统中, 其他任何人都不应访问该个人资料。显然, 普通系统应该提供安全性, 以确保访问概要文件的用户与所有者相同, 但这只是你可以理解的一个示例。现在, 最好使用字母数字字符串来标识系统中的ID(至少对于人类而言, 这不是那么容易阅读和创建的), 例如:

http://imaginarysystem.com/profiles/xzq85hj

该字母数字字符串虽然是相同的数字(2), 但已被”屏蔽”, 因此一旦用户以http://imaginarysystem.com/profiles/xzq85hj调用URL, 就可以将其”取消屏蔽”, 其中xzq85hj应该由我们的脚本转换为2(因为每个数据库都使用数字而不是字符串来真正快速地执行查询)。请注意, 你不应将此库用作安全工具, 也不要对敏感数据进行编码。这不是加密库。

YouTube或其他任何类型的URL Shorteners服务(如TinyURL, Google URL Shortener等)都遵循此样式。此外, 它还可以保护恶意爬虫, 这些爬虫只想尝试通过尝试URL中所有可能的数字来复制你网站的内容, 例如http://imaginarysystem.com/profiles/ {此处的循环数, 例如3、4、5, 6}。

实现

尽管听起来很容易实现, 因为我们只需要哈希一个数字就可以了, 但是如果你想以Symfony样式正确实现它, 那将需要一些时间。按着这些次序:

1.安装hashids库

你需要做的第一件事是在你的Symfony项目中安装hashids库。 Hashids是一个小型PHP库, 用于从数字生成类似YouTube的ID。当你不想向用户公开数据库ID时, 请使用它。

要安装此库, 请打开一个终端, 切换到项目目录并执行以下命令:

composer require hashids/hashids

另外, 你可以修改composer.json文件并手动添加依赖项:

{
    "require": {
        "hashids/hashids": "^2.0"
    }, }

然后运行composer install。安装完成后, 你将可以在项目中使用该库。如果你想了解有关此库的更多信息, 请在此处访问Github中的官方存储库。

2.在控制器中的用法

hashids库的工作方式如下, 你将创建一个新的Hashids实例, 其构造函数将随机性或熵字符串作为第一个参数, 并根据此字符串生成哈希, 此外, 你还可以指定第二个参数。请参见以下蒙版示例:

<?php 

use Hashids\Hashids;

// Set padding of string to 10 and entropy string "My Project"
$hashids = new Hashids('My Project', 10);
echo $hashids->encode(1); // jEWOEVpQx3

// Set padding of string to 10 and entropy string "My Other Project"
$hashids = new Hashids('My Other Project', 10);
echo $hashids->encode(1); // NA4ByeBWQp

然后, 如果要将哈希转换为数字表示形式(再次将字符串转换为数字1):

<?php 

use Hashids\Hashids;

// Set padding of string to 10 and entropy string "My Project"
$hashids = new Hashids('My Project', 10);
echo $hashids->decode("jEWOEVpQx3")[0]; // 1

// Set padding of string to 10 and entropy string "My Other Project"
$hashids = new Hashids('My Other Project', 10);
echo $hashids->decode("NA4ByeBWQp")[0]; // 1

请注意, 解码过程返回一个数组, 因此我们通过第一个(通常是唯一的)索引访问结果。

现在, 在你的Symfony项目中, 显然不会创建该类的新实例, 然后在需要它的每个控制器上写入相同的熵字符串和填充值。相反, 我们建议你为其创建一个新的Symfony服务。在此服务中, 我们将处理Hashids库, 并创建2种方法, 一种将用于创建哈希, 另一种将哈希转换为数字表示。我们将在服务中生成掩码时使用的熵字符串将是在parameters.yml文件中声明的自定义字符串和一个填充数字(用于定义所生成掩码的长度), 但是你可以定义这些值如果需要, 直接在班上。

第一步, 我们将在参数文件(app / config / parameters.yml)中或根据config.yml文件中的最佳做法, 使用以下标识符将你的熵字符串和生成的哈希值的长度定义为自定义参数。 :

重要

这些值将始终相同, 一旦在应用程序中设置它们就无法更改, 否则URL掩码也会更改。

# Add 2 new parameters
parameters:
    url_hasher_entropy: randomstring
    url_hasher_padding: 10

请记住, 你可以(不一定)在名为parameters.yml.dist的文件中设置这些参数, 该文件存储了应用程序的配置参数的规范列表(在此处了解更多信息)。创建新参数后, 继续创建新的Symfony服务, 在这种情况下, 创建一个标识为IdHasher(IdHasher.php)的类, 并将以下代码放入其中(根据你的应用更改名称空间):

注意

这是Hashids(编码和解码)的基本实现, 如果需要, 可以使其变得更加复杂。

<?php

// The namespace needs to be changed according to your needs
namespace AppBundle\Services;

use Symfony\Component\Config\Definition\Exception\Exception;
use Hashids\Hashids;

class IdHasher{
    private $HashIdsInstance;

    /**
     * The constructor expects the entropy string and the padding for the Hashids class
     * parameters declared as url_hasher_entropy and url_hasher_padding in parameters.yml
     *
     * @param $entropyString {String}
     * @param $hashPadding {Integer}
     */
    public function __construct($entropyString, $hashPadding)
    {
        $this->HashIdsInstance = new Hashids($entropyString, $hashPadding);
    }

    public function encode($number){
        return $this->HashIdsInstance->encode($number);
    }

    public function decode($hash){
        $result = $this->HashIdsInstance->decode($hash);
        
        return empty($result) ? null : $result[0];
    }
}

在此示例中, 该文件是在symfonyproject / src / AppBundle / Services中创建的。请注意, 我们将在服务中注入2个静态参数。现在该服务已存在, 我们只需要注册它并注入先前创建的自定义参数即可。打开项目的services.yml文件并注册服务:

services:
    # Name of the service id_hasher
    id_hasher:
        # Path of the previously created class
        class: AppBundle\Services\IdHasher
        # Inject the custom parameters as arguments for the IdHasher class
        arguments: ["%url_hasher_entropy%" , "%url_hasher_padding%"]

可能需要使用id_hasher标识符来提供我们的服务, 保存更改, 清除项目的缓存, 并且现在应该注册该服务, 你可以从控制器中使用它, 例如:

<?php 

// Retrieve our created hasher service
// $this->get = the service container
$hasherService = $this->get("id_hasher");

// With our secret generates : bJ0d4Wn3x8 (will vary on your project as the secret is different)
echo $hasherService->encode(1);

// Decode the hash generated by the encode function 
echo $hasherService->decode("bJ0d4Wn3x8"); // 1

很容易吧?有了它, 你将能够根据需要生成和使用数字的哈希值, 以生成自定义路由等。如果解码函数返回null, 则意味着不能将提供的”哈希”转换为有效值数。

控制器示例

现在, 让我们实施一些你会更容易理解的东西。我们将创建一个VideosController, 该控制器只有2条路线, 一条路线会将URL中检索到的数字ID转换为其哈希值(过程路线), 并将自动重定向到show视图:

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class VideosController extends Controller
{
    public function generateAction($id){
        // Retrieve our created hasher service
        $hasherService = $this->get("id_hasher");

        // Generate hash of the integer number
        $hashId = $hasherService->encode($id);

        // Send a response
        // http://demo/app_dev.php/videos/generate/1 => generates hashId bJ0d4Wn3x8
        // return new Response("The hash for your video with numeric id $id is : ".$hashId);

        // Or you can make it interesting and generate a route directly to the showAction (http://demo/app_dev.php/videos/show/{the generated hash})
        return $this->redirectToRoute('videos_show', [
            'hashId' => $hashId
        ]);
    }

    public function showAction($hashId){
        // Retrieve our created hasher service
        $hasherService = $this->get("id_hasher");

        // Convert the hash to its integer representation
        $numericId = $hasherService->decode($hashId);

        // Send a response
        // http://demo/app_dev.php/videos/show/bJ0d4Wn3x8 => prints "1" as numeric id again :)
        return new Response("The number of your video with id $hashId is : ".$numericId);
    }
}

以下控制器的路由在routing.yml文件中定义, 其结构如下:

videos_generate:
    path:      /videos/generate/{id}
    defaults:  { _controller: AppBundle:Videos:generate }

videos_show:
    path:      /videos/show/{hashId}
    defaults:  { _controller: AppBundle:Videos:show }

该控制器的工作方式非常简单, 你可以使用以下示例URL http:// demo / videos / generate / 189访问generate操作。现在, 一旦用户访问了URL, 他将被自动重定向到将189转换为其哈希值的show视图, 在我们的示例中为http:// demo / videos / show / bJ0d4rzE3x。

如何处理散列以生成路由, 重定向或将散列存储在数据库中的决定(效率不高)完全取决于你。

3.使用Twig中的哈希

如果你很明智并且决定不将服务生成的哈希存储在数据库中, 因为这是不必要的, 那你可能已经想到, 如何在视图上创建带有哈希值的路由?好问题!如你所见, 到目前为止, 我们只能在控制器中或在项目中使用PHP代码生成哈希, 而不能在Twig中生成哈希。因此, 你需要创建一个新的Twig扩展, 以公开新功能, 即url_id_hasher_encode和url_id_hasher_decode(可以根据需要更改其名称)。

你需要创建的扩展非常简单, 我们只需要与Twig扩展中先前创建的服务(步骤2)相同, 并为twig中的编码和解码功能编写一些包装即可。我们将仅创建2个Twig函数, 这些函数希望将要编码或解码的字符串或数字作为唯一参数(取决于你使用的函数)。

要创建扩展, 请创建一个新文件, 即TwigExtensions.php, 其内容如下:

<?php
// The namespace according to the bundle and the path
namespace AppBundle\Extensions;

use Symfony\Component\DependencyInjection\Container;
// Include the IdHasher class to instantiate in the constructor
use AppBundle\Services\IdHasher;

// The Name of your class
class TwigExtensions extends \Twig_Extension
{
    protected $id_hasher;

    public function getFunctions()
    {
        return array(
            new \Twig_SimpleFunction('url_id_hasher_encode', array($this, 'urlIdHasherEncode')), new \Twig_SimpleFunction('url_id_hasher_decode', array($this, 'urlIdHasherDecode'))
        );
    }
    
    public function __construct(IdHasher $hasherService)
    {
        $this->id_hasher = $hasherService;
    }

    /**
     * Declare encode function
     *
     * @param $value {Integer || String} Number to convert into hash
     */
    public function urlIdHasherEncode($value){
        return $this->id_hasher->encode($value);
    }

    /**
     * Declare decode function
     *
     * @param $value {Integer || String} NHash to convert into number
     */
    public function urlIdHasherDecode($value){
        return $this->id_hasher->decode($value);
    }
    
    public function getName()
    {
        return 'TwigExtensions';
    }
}

注意

如果已有TwigExtensions文件可用, 则只能复制函数以对数字进行哈希处理, 反之亦然, 但是不要忘记将创建的服务注入Twig扩展名。

扩展上的特殊功能是在Twig中注册为url_id_hasher_encode和url_id_hasher_decode的编码和解码PHP函数。如你所见, 它接收id_hasher服务(在步骤2中创建), 并根据其参数对字符串进行编码或解码。现在, 在services.yml文件中注册扩展名(根据你的位置更改类):

services:
    twig.extension:
    # the namespace with the name of your twig extension class
        class: AppBundle\Extensions\TwigExtensions
        arguments: 
            service_container : "@id_hasher"
        tags:
            -  { name: twig.extension }

保存更改, 清除项目的缓存, 现在可以在Twig视图中使用这些功能, 如以下示例所示:

{% extends 'base.html.twig' %}

{% block body %}
    {# Note that according to your custom parameters the hashes will change#}
    {# Prints bJ0d4Wn3x8 #}
    {{ url_id_hasher_encode(1)}}

    {# Prints 1#}
    {{ url_id_hasher_decode("bJ0d4Wn3x8")}}
    
{% endblock %}

以及如何生成路线的示例(使用先前注册的路线videos_show):

{% extends 'base.html.twig' %}

{% block body %}
    {# http://demo/videos/show/bJ0d4rzE3x #}
    {% set LinkUrl = path("videos_show", { 'hashId': url_id_hasher_encode(1) }) %}

    <a href="{{LinkUrl}}">
        Redirect to video !
    </a>
{% endblock %}

编码愉快!

赞(0)
未经允许不得转载:srcmini » 如何在Symfony 3中基于数字值(YouTube或URL Shortener样式)创建非顺序唯一ID

评论 抢沙发

评论前必须登录!