jelix/profiles

管理访问和凭证数据的API

v1.2.1 2023-01-22 18:35 UTC

This package is auto-updated.

Last update: 2024-09-05 10:07:44 UTC


README

管理包含访问和凭证数据的配置文件的API。

一个配置文件包含访问服务的必要信息。服务可以是数据库,例如一个Web API。在一个应用程序内部,您可以针对同一类型的服务有多个配置文件。

JelixProfiles 允许在ini文件中指定这些配置文件。所有的凭证都将存储在单个文件中。当您的应用程序的一个组件(让我们称它为“连接器”)需要有关配置文件的信息时,它将使用JelixProfiles。

安装

您可以使用Composer进行安装。在您的项目中

composer require "jelix/profiles"

配置

配置文件数据应存储在ini文件中。此文件包含应用程序组件所需连接参数:SQL和NoSQL数据库、SOAP Web服务、缓存等。

配置文件参数

每个部分对应一个配置文件。一个配置文件是一个连接参数集。部分名称由两个名称组成,用“:”分隔。

  • 第一个名称是连接类型的名称,(通常对应组件名称)
  • 第二个名称是您选择的名称。然而,有两个名称具有特殊含义:default 表示在未提供配置文件名称时使用默认配置文件。还有 __common__,如下所述。

连接参数部分的内部内容。

以下是一个JelixDatabase组件的示例(它允许访问SQL数据库)

[jdb:default]
driver=mysqli
host=localhost
login= mylogin
password=mypassword

配置文件别名

您可以为某些配置文件定义一些别名,即对配置文件有多个名称。这在两个库使用不同的配置文件名称,但您希望这些库使用相同的连接参数时很有用。

别名在仅包含连接类型名称的章节中定义。以下是一个使用JelixDatabase的示例,为默认配置文件定义了别名“jacl2_profile”

[jdb]
jacl2_profile = default

[jdb:default]
driver=mysqli
host=localhost
login= mylogin
password=mypassword

别名不应链接到另一个别名。

通用参数

您可以定义适用于同一类型所有配置文件的通用参数。这避免了在每个配置文件中重复它们。为此,您必须在特殊的配置文件 __common__ 中声明它们。

例如,如果所有SQL数据库的连接都必须持久且都在同一服务器上

[jdb:__common__]
host=my.server.example.com
persistant=on

您当然可以在配置文件中重新定义这些参数。

使用环境变量

ini文件由 parse_ini_file 函数读取,因此有特定的语法来指示来自ini文件外部的值。

  • 您可以使用PHP常量
# somewhere in a file included by your index.php
define("SOMETHING", "hello world");

在您的某个ini文件中

foo = SOMETHING

然后 foo 将具有值 "hello world"

  • 您可以使用环境变量
# variables set in the environment of PHP-FPM, PHP-CLI, or Apache (with the PHP module)
MYAPP_MYSQL_LOGIN=admin
MYAPP_MYSQL_PASSWORD=Sup3Rp4ssw0rd!

例如,在您的ini文件中,您必须使用此语法 ${VARIABLE_NAME}

[jdb:default]
driver=mysqli
login= ${MYAPP_MYSQL_LOGIN}
password= ${MYAPP_MYSQL_PASSWORD}

使用方法

您应使用 ProfilesReader 对象,该对象将读取ini文件,并将结果存储到 ProfilesContainer 对象中。使用此对象,您将能够获取配置文件的数据。

如果您有这种类型的 profiles.ini 文件

[jdb:default]
driver=mysqli
host=localhost
login= mylogin
password=mypassword

在使用JelixProfiles的组件代码中

$iniFile = '/somewhere/profiles.ini';
$cacheFile = '/somewhere/profiles.cache.json';

$reader = new \Jelix\Profiles\ProfilesReader();

/** @var \Jelix\Profiles\ProfilesContainer $profiles */
$profiles = $reader->readFromFile($iniFile, $cacheFile);

// get the profile named 'foo' for the type 'jdb'
$profile = $profiles->get('jdb', 'default');

$myDatabaseConnection = new Database(
    $profile['driver'],
    $profile['host'],
    $profile['login'],
    $profile['password']
);

请注意,您必须向 readFromFile 提供一个缓存文件。这允许JelixProfiles在加载ini文件期间保存额外的计算参数。

虚拟配置文件

您可以使用未在ini文件中声明的配置文件。因此,您可以使用只在执行期间知道的信息的连接。

虚拟配置文件必须在使用您的组件之前创建。使用 createVirtualProfile() 方法的对象 ProfilesContainer 并传递连接类型的名称、一个名称和参数数组。

示例

// let's retrieve a profiles container
$iniFile = '/somewhere/profiles.ini';
$cacheFile = '/somewhere/profiles.cache.json';
$reader = new \Jelix\Profiles\ProfilesReader();
/** @var \Jelix\Profiles\ProfilesContainer $profiles */
$profiles = $reader->readFromFile($iniFile, $cacheFile);


// now create our virtual profile
$params = array(
   'driver'=>'mysqli',
   'host'=>'localhost',
   'database'=>'jelix',
   'user'=>'toto',
   'password'=>'blabla',
   'persistent'=>false,
   'force_encoding'=>true
);
$profiles->createVirtualProfile('jdb', 'my_profil', $params);

// somewhere else
$profile = $profiles->get('jdb', 'my_profil');

//...

当然,在__common__配置文件中定义的所有参数都适用于虚拟配置文件。

使用插件检查参数

连接器,使用配置文件的对象,可能期望有特定的参数。在使用参数之前,它可能需要检查参数是否正确。它甚至可能需要根据在给定参数中找到的值“计算”一些其他参数。

为了避免每次连接器实例化时都执行这些检查或计算,JelixProfiles提供了一个解决方案,在首次读取配置文件时处理这些事情,然后最终参数存储在配置文件的缓存文件中。

您可以提供一个插件,用于为特定类别的配置文件执行这些检查和计算。这是一个应该继承自Jelix\Profiles\ReaderPlugin的类,并实现了接收参数数组作为参数的consolidate方法,并返回完整的配置文件。

示例

// example of a plugin that process a profile containing access parameters to an SQL database
class myDbPlugin extends \Jelix\Profiles\ReaderPlugin
{

    protected function consolidate($profile)
    {
        $newProfile = $profile;
        // Check that the `host` parameter does exist
        if (!isset($profile['driver']) || $profile['driver'] == '' ||
            !isset($profile['host']) || $profile['host'] == '' ||
            !isset($profile['database']) || $profile['database'] == ''
        ) {
            throw new  \Exception('host or database are missing from the profile');
        }
        
        // check if port is present, if not, let's set a default value
        if (!isset($profile['port']) {
            $profile['port'] = 1234;        
        }

        if ($driver == 'pgsql') {
            // let's generate a connection string
            $newProfile['connectionstr'] = 'host='.$profile['host'].';port='.$profile['port'].'database='.$profile['database'];        
        }

        // here you probably want to do more checks etc, but for the example, it is enough.

        // return a new profile, that is ready to be used by your database connection object without checking
        // parameters or calculate connectionstr, at each http requests, because all these parameters will be stored
        // into a cache by ProfilesReader
        return $newProfile;
    }
}

接下来,您必须通过给出每个类别的插件列表来指示配置文件读取器。

$reader = new \Jelix\Profiles\ProfilesReader([
    // category name => plugin class name
    'db' => 'myDbPlugin'
]);

另一种方法是提供一个回调函数,该函数将返回给定类别的插件。如果您的插件具有特定的构造函数,或者类以特定的方式加载,这很有用。

class myDbPlugin extends \Jelix\Profiles\ReaderPlugin
{
    // for the example, let's redefine the constructor to have a different constructor than ReaderPlugin...
    public function __construct() {}
 
    // ...   
}

$reader = new \Jelix\Profiles\ProfilesReader(function($category) {
    if ($category == 'db') {
        return new myDbPlugin();
    }
    return null;
});

使用插件自动实例化连接器

我们看到了您可以检索配置文件,然后您必须将其提供给您的连接器对象。

$profile = $profiles->get('jdb', 'default');

$myDatabaseConnection = new Database(
    $profile['driver'],
    $profile['host'],
    $profile['login'],
    $profile['password']
);

ProfilesContainer有一个方法可以自动实例化与配置文件对应的连接器,并且它还允许在整个HTTP请求过程中使用相同的连接器对象。这使得您的代码更轻量。

$myDatabaseConnection = $profiles->getConnector('jdb', 'default');

为了使用此方法,插件必须实现Jelix\Profiles\ProfileInstancePluginInterface。它有两个方法,getInstanceForPool($name, $profile)closeInstanceForPool($name, $instance)

第一个方法是实例化连接器对象。它由getConnector调用。第二个方法应该终止连接器对象。例如,如果连接器对象维护到数据库的连接,closeInstanceForPool()应调用其关闭连接的方法。

class myDbPlugin extends \Jelix\Profiles\ReaderPlugin implements 
{
    protected function consolidate($profile)
    {
        // ...
    }
    
    public function getInstanceForPool($name, $profile)
    {
        return new Database(
          $profile['driver'],
          $profile['host'],
          $profile['login'],
          $profile['password']
        );
    }
    
    /**
     * @param string $name
     * @param Database $instance
     * @return void
     */
    public function closeInstanceForPool($name, $instance)
    {
        $instance->close()
    }
}