Open CaMer0n opened 12 months ago
e107_config.php
v3 FormatThis request for comments proposes a new format for the e107_config.php
file, which is used to store the configuration of an e107 installation.
The e107 v2 configuration file is made of unstructured and inflexible variables and some additional code that defines constants to modify global behavior.
This is the template of the e107 v2 configuration file:
<?php
/**
* e107 website system
*
* Copyright (C) 2008-{$copyrightYear} e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
*
* e107 configuration file
*
* This file has been generated by the installation script on {$rfc2822Date}.
*/
$mySQLserver = '{{ mySQLserver }}';
$mySQLuser = '{{ mySQLuser }}';
$mySQLpassword = '{{ mySQLpassword }}';
$mySQLdefaultdb = '{{ mySQLdefaultdb }}';
$mySQLport = '{{ mySQLport }}';
$mySQLprefix = '{{ mySQLprefix }}';
$mySQLcharset = 'utf8mb4';
$ADMIN_DIRECTORY = 'e107_admin/';
$FILES_DIRECTORY = 'e107_files/';
$IMAGES_DIRECTORY = 'e107_images/';
$THEMES_DIRECTORY = 'e107_themes/';
$PLUGINS_DIRECTORY = 'e107_plugins/';
$HANDLERS_DIRECTORY = 'e107_handlers/';
$LANGUAGES_DIRECTORY = 'e107_languages/';
$HELP_DIRECTORY = 'e107_docs/help/';
$MEDIA_DIRECTORY = 'e107_media/';
$SYSTEM_DIRECTORY = 'e107_system/';
// -- Optional --
// define('e_DEBUG', true); // Enable debug mode to allow displaying of errors
// define('e_HTTP_STATIC', 'https://static.mydomain.com/'); // Use a static subdomain for js/css/images etc.
// define('e_MOD_REWRITE_STATIC', true); // Rewrite static image urls.
// define('e_LOG_CRITICAL', true); // log critical errors but do not display them to user.
// define('e_GIT', 'path-to-git'); // Path to GIT for developers
// define('X-FRAME-SAMEORIGIN', false); // Option to override X-Frame-Options
// define('e_PDO, true); // Enable PDO mode (used in PHP > 7 and when mysql_* methods are not available)
This template has the following limitations:
The equivalent configuration file in the new format would look like this:
<?php
/**
* e107 website system
*
* Copyright (C) 2008-{$copyrightYear} e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (https://www.gnu.org/licenses/gpl.txt)
*
* Site configuration file
*/
class E107Config extends BaseSiteConfig
{
function getDatabase(): DatabaseConnector
{
return new MysqlTcpConnector(
user: '{{ mySQLuser }}',
password: '{{ mySQLpassword }}',
host: '{{ mySQLserver }}',
port: '{{ mySQLport }}',
dbname: '{{ mySQLdefaultdb }}',
charset: 'utf8mb4',
prefix: '{{ mySQLprefix }}',
);
}
#[ArrayShape(SiteConfig::PATHS_ARRAY_SHAPE)]
function getPaths(): array
{
return [
'admin' => 'e107_admin/',
'core' => 'e107_system/',
'handlers' => 'e107_handlers/',
'images' => 'e107_images/',
'languages' => 'e107_languages/',
'media' => 'e107_media/',
'plugins' => 'e107_plugins/',
'system' => 'e107_system/',
'themes' => 'e107_themes/',
'web' => 'e107_web/',
'docs' => 'e107_docs/',
'files' => 'e107_files/',
];
}
function defineGlobals(): void
{
// define('e_DEBUG', true); // Enable debug mode to allow displaying of errors
// define('e_HTTP_STATIC', 'https://static.mydomain.com/'); // Use a static subdomain for js/css/images etc.
// define('e_MOD_REWRITE_STATIC', true); // Rewrite static image urls.
// define('e_LOG_CRITICAL', true); // log critical errors but do not display them to user.
// define('e_GIT', 'path-to-git'); // Path to GIT for developers
// define('X-FRAME-SAMEORIGIN', false); // Option to override X-Frame-Options
// define('e_PDO, true); // Enable PDO mode (used in PHP > 7 and when mysql_* methods are not available)
}
}
return new E107Config();
This template has the following advantages:
DatabaseConnector
interface. Plugins or other core components can provide their own implementations of the interface to support different database types or connection methods.defineGlobals()
method allows us to defer and disable the execution of this code in the future. Administrators can then test their installation with the legacy code disabled and report any issues.To use this structured approach, the following interface is defined prior to loading e107_config.php
:
/**
* Interface for the return value of including e107_config.php
*/
interface SiteConfig
{
const PATHS_ARRAY_SHAPE = [
// Current paths
'admin' => 'string', // Admin area files
'core' => 'string', // System assets
'handlers' => 'string', // System backend framework
'images' => 'string', // System images
'languages' => 'string', // Locale files
'media' => 'string', // Uploaded files
'plugins' => 'string', // System plugins
'system' => 'string', // Runtime-generated site files
'themes' => 'string', // System themes
'web' => 'string', // Frontend web libraries
// Legacy paths
'docs' => 'string', // Admin documentation
'files' => 'string', // e107 v1/v2 supplementary files
];
/**
* Base Paths
*
* Relative paths to the directories of each kind of e107 resource
*
* e107 will append site-specific paths to these base paths.
*
* @return array Associative array of relative paths
*/
#[ArrayShape(SiteConfig::PATHS_ARRAY_SHAPE)]
function getPaths(): array;
/**
* Database Connection Settings
*
* How to connect to the database
*
* @return DatabaseConnector The database connection settings
*/
function getDatabase(): DatabaseConnector;
/**
* Set legacy global constants
*
* @deprecated v2.4.0 Global constants are being phased out in favor of configurable settings elsewhere.
*
* @example
* define('e_DEBUG', true); // Enable debug mode to allow displaying of errors
* define('e_HTTP_STATIC', 'https://static.mydomain.com/'); // Use a static subdomain for js/css/images etc.
* define('e_MOD_REWRITE_STATIC', true); // Rewrite static image urls.
* define('e_LOG_CRITICAL', true); // log critical errors but do not display them to user.
* define('e_GIT', 'path-to-git'); // Path to GIT for developers
* define('X-FRAME-SAMEORIGIN', false); // Option to override X-Frame-Options
* define('e_PDO, true); // Enable PDO mode (used in PHP > 7 and when mysql_* methods are not available)
*/
function defineGlobals(): void;
}
To shield the concrete implementation from new methods that might be added to the interface in the future, the interface is extended by an abstract class:
/**
* Dummy class implementing the {@see SiteConfig} interface
*/
abstract class BaseSiteConfig implements SiteConfig
{
/**
* @inheritDoc
*/
abstract function getDatabase(): DatabaseConnector;
/**
* @inheritDoc
*/
abstract function getPaths(): array;
/**
* @inheritDoc
*/
abstract function defineGlobals(): void;
}
We could go a step further and inherit sensible defaults, but this would obfuscate what configurable options are available in the concrete implementation in e107_config.php
.
Such a default implementation could look like this:
/**
* Partial {@see SiteConfig} implementation with default paths and no global constants
*
* {@see SiteConfig::getDatabase()} must be implemented by the concrete class.
*/
abstract class DefaultSiteConfig implements SiteConfig
{
const DEFAULT_PATH_ADMIN = 'admin/';
const DEFAULT_PATH_CORE = 'core/';
const DEFAULT_PATH_HANDLERS = 'handlers/';
const DEFAULT_PATH_IMAGES = 'images/';
const DEFAULT_PATH_LANGUAGES = 'languages/';
const DEFAULT_PATH_MEDIA = 'media/';
const DEFAULT_PATH_PLUGINS = 'plugins/';
const DEFAULT_PATH_SYSTEM = 'system/';
const DEFAULT_PATH_THEMES = 'themes/';
const DEFAULT_PATH_WEB = 'web/';
const DEFAULT_PATH_DOCS = 'docs/';
const DEFAULT_PATH_FILES = 'files/';
protected function pathPrefix(): string
{
return "e107_";
}
/**
* @inheritDoc
*/
#[ArrayShape(SiteConfig::PATHS_ARRAY_SHAPE)]
function getPaths(): array
{
$pathPrefix = $this->pathPrefix();
return [
'admin' => $pathPrefix . self::DEFAULT_PATH_ADMIN,
'core' => $pathPrefix . self::DEFAULT_PATH_CORE,
'handlers' => $pathPrefix . self::DEFAULT_PATH_HANDLERS,
'images' => $pathPrefix . self::DEFAULT_PATH_IMAGES,
'languages' => $pathPrefix . self::DEFAULT_PATH_LANGUAGES,
'media' => $pathPrefix . self::DEFAULT_PATH_MEDIA,
'plugins' => $pathPrefix . self::DEFAULT_PATH_PLUGINS,
'system' => $pathPrefix . self::DEFAULT_PATH_SYSTEM,
'themes' => $pathPrefix . self::DEFAULT_PATH_THEMES,
'web' => $pathPrefix . self::DEFAULT_PATH_WEB,
'docs' => $pathPrefix . self::DEFAULT_PATH_DOCS,
'files' => $pathPrefix . self::DEFAULT_PATH_FILES,
];
}
/**
* @inheritDoc
*/
function defineGlobals(): void
{
}
}
DatabaseConnector
The DatabaseConnector
is a simple interface that returns the database abstraction layer (DBAL):
interface DatabaseConnector
{
/**
* Get a new database connection
*
* @return e_db The database abstraction layer
*/
function connect(): e_db;
}
Implementations will be autoloaded from the core in e107_handlers/Database/Connection
or from plugins.
The core will provide the following implementations:
MysqlConnector < DatabaseConnector
for MySQL or MariaDB connections
MysqlTcpConnector < MysqlConnector
for MySQL connections via TCPMysqlTlsConnector < MysqlTcpConnector
for encrypted MySQL connections via TCPMysqlSocketConnector < MysqlConnector
for MySQL connections via a UNIX socketDsnConnector < DatabaseConnector
for connections from a DSN string as an alternative and compact way to define the database connection. (A DSN string might look like: mysql://user:password@localhost:3306/database?charset=utf8mb4
)Although not in the scope of this RFC, this plug-in architecture could be extended to support more database types. Hypothetical examples could be:
PgsqlConnector < DatabaseConnector
for PostgreSQL connectionsSqliteConnector < DatabaseConnector
for SQLite file connectionsSqlsrvConnector < DatabaseConnector
for Microsoft SQL Server connectionsOracleConnector < DatabaseConnector
for Oracle connectionsDb2Connector < DatabaseConnector
for IBM DB2 connectionsUsing just the new configuration file format in the core would be simple:
$config = include(e_ROOT . 'e107_config.php');
// Connect to the database
$db = $config->getDatabase()->connect();
// Get the handler path
$handlerPath = $config->getPaths()['handlers'];
To transition from the old configuration file format to the new one, we will need to detect which format is used and wrap the old configuration file values in an adapter:
$config = include(e_ROOT . 'e107_config.php');
if (!($config instanceof SiteConfig)) {
$config = new V2ConfigAdapter();
}
V2ConfigAdapter
will implement the SiteConfig
interface and wrap the old configuration file values:
/**
* Adapter for the old e107 v2 configuration file format
*/
class V2ConfigAdapter implements SiteConfig
{
protected const DATABASE_CREDENTIAL_DEFAULTS = [
'prefix' => '',
'port' => '3306',
'charset' => 'utf8mb4',
];
protected const LEGACY_DATABASE_VARIABLES = [
'prefix' => 'mySQLprefix',
'host' => 'mySQLserver',
'port' => 'mySQLport',
'name' => 'mySQLdefaultdb',
'username' => 'mySQLuser',
'password' => 'mySQLpassword',
'charset' => 'mySQLcharset',
];
protected const LEGACY_PATH_VARIABLES = [
'admin' => 'ADMIN_DIRECTORY',
'core' => 'CORE_DIRECTORY',
'docs' => 'DOCS_DIRECTORY',
'help' => 'HELP_DIRECTORY',
'files' => 'FILES_DIRECTORY',
'handlers' => 'HANDLERS_DIRECTORY',
'images' => 'IMAGES_DIRECTORY',
'languages' => 'LANGUAGES_DIRECTORY',
'media' => 'MEDIA_DIRECTORY',
'plugins' => 'PLUGINS_DIRECTORY',
'system' => 'SYSTEM_DIRECTORY',
'themes' => 'THEMES_DIRECTORY',
'web' => 'WEB_DIRECTORY',
];
/**
* @inheritDoc
*/
function getDatabase(): DatabaseConnector
{
$credentials = self::DATABASE_CREDENTIAL_DEFAULTS;
foreach (self::LEGACY_DATABASE_VARIABLES as $key => $legacyVariable) {
$credentials[$key] = $$legacyVariable;
}
return new DsnConnector(
dsn: sprintf(
'mysql://%s:%s@%s:%s/%s?charset=%s',
$credentials['username'],
$credentials['password'],
$credentials['host'],
$credentials['port'],
$credentials['name'],
$credentials['charset'],
),
prefix: $credentials['prefix'],
);
}
/**
* @inheritDoc
*/
#[ArrayShape(SiteConfig::PATHS_ARRAY_SHAPE)]
function getPaths(): array
{
$paths = [];
foreach (self::LEGACY_PATH_VARIABLES as $key => $legacyVariable) {
$paths[$key] = constant($legacyVariable);
}
return $paths;
}
/**
* @inheritDoc
*/
function defineGlobals(): void
{
include_once(e_ROOT . 'e107_config.php');
}
}
The old configuration file format can likely be migrated to the new one automatically by a script that reads the old configuration file and replaces it with the new one.
A PHP parser is not required because the configuration loaded by the adapter (V2ConfigAdapter
) can be rewritten into the new format template, and all the other code can be wrapped within the SiteConfig::defineGlobals()
method.
Thank you for this @Deltik .
Since e107's inception by Jalist over 20 years ago, I, and the other developers through the years, have tried our best to follow his lead, by maintaining the KISS principal "keep-it-simple, stupid!" that he worked by. Not just with the user experience of e107, but also in how it is coded. The goal has always been to minimize complexity, take a minimalist approach, while still trying to maintain a balance, so that the CMS remains both flexible and powerful. Particularly in the areas of plugins, themes and files that users may need to edit, we've always strived to reduce complexity, in order to make the code feel intuitive, even to a non-coder or novice.
So, while I appreciate all the time and effort that must have gone into your proposal, after careful consideration, I don’t find it to be a good fit for e107.
btw. I also took some time to research the format of similar config files for other CMSes while considering your proposal. Some of them are quite powerful, and function with multiple types of databases. Here were the results:
/** The name of the database for WordPress */
define('DB_NAME', 'database_name');
/** MySQL database username */
define('DB_USER', 'database_username');
/** MySQL database password */
define('DB_PASSWORD', 'database_password');
/** MySQL hostname */
define('DB_HOST', 'localhost');
/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');
/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
public $dbtype = 'mysqli';
public $host = 'localhost';
public $user = 'database_user';
public $password = 'database_password';
public $db = 'database_name';
public $dbprefix = 'jos_';
<?php
/**
/**
/**
/**
4. **Magento**
...
'db' => [
'table_prefix' => '',
'connection' => [
'default' => [
'host' => 'localhost',
'dbname' => 'magento',
'username' => 'root',
'password' => ‘root’,
'model' => 'mysql4',
'engine' => 'innodb',
'initStatements' => 'SET NAMES utf8;',
'active' => '1'
]
]
],
...
define('_DB_SERVER_', 'localhost');
define('_DB_NAME_', 'database_name');
define('_DB_USER_', 'database_user');
define('_DB_PASSWD_', 'database_password');
define('_DB_PREFIX_', 'ps_');
// DB
define('DB_DRIVER', 'mysqli');
define('DB_HOSTNAME', 'localhost');
define('DB_USERNAME', 'user');
define('DB_PASSWORD', 'pass');
define('DB_DATABASE', 'opencart');
define('DB_PORT', '3306');
define('DB_PREFIX', 'oc_');
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['dbname'] = 'your-database-name';
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['user'] = 'database-user';
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['password'] = 'password';
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['port'] = '3306';
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['host'] = 'localhost';
ExpressionEngine utilizes a config.php
file for management of many settings. Entries look similar to this:
$config['app_version'] = "305";
$config['install_lock'] = "";
$config['license_number'] = "";
$config['debug'] = "1";
$config['cp_url'] = "";
$config['doc_url'] = "http://ellislab.com/expressionengine/user-guide/";
$config['site_label'] = 'My Site';
$config['cookie_prefix'] = '';
In Concrete5, the configuration details are stored as PHP constants inside the site.php
file located in the config
directory. Entries look like the following:
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'your_db_username');
define('DB_PASSWORD', 'your_db_password');
define('DB_DATABASE', 'your_database');
define('PASSWORD_SALT', 'random_string_for_encryption');
Craft CMS utilizes a .env
file to store sensitive configuration details. Entries in the .env
file are typically as follows:
# The environment Craft is currently running in
ENVIRONMENT="dev"
# The secure key Craft will use for hashing and encrypting data
SECURITY_KEY="your_security_key"
# The data source name (DSN) for connecting to the database
DB_DSN="mysql:host=localhost;dbname=craft"
# The database username
DB_USER="your_db_username"
# The database password
DB_PASSWORD="your_db_password"
# The database table prefix
DB_TABLE_PREFIX="craft"
In this flat-file CMS, the system configuration is stored as a YAML file in system/config/system.yaml
. An example looks as follows:
home:
alias: '/home'
cache:
enabled: true
system:
pages:
theme: mytheme
cache:
gzip: true
Be aware that f.e. lgsl plugin uses e107_config this way:
case "e107":
@include "{$lgsl_file_path}../../../e107_config.php";
$lgsl_config['db']['server'] = $mySQLserver;
$lgsl_config['db']['user'] = $mySQLuser;
$lgsl_config['db']['pass'] = $mySQLpassword;
$lgsl_config['db']['db'] = $mySQLdefaultdb;
$lgsl_config['db']['prefix'] = $mySQLprefix;
break;
Tested: With new config there are issues if you renamed folder's names.
Motivation
Currently it's a mess of different formats.
Proposed Solution
Return a multi-dimensional array. As an example:
or an Object:
Alternatives
-
Additional Context
No response