From 7ca5dea46271ecc7ea7f0c0cb819f9e53945f4c6 Mon Sep 17 00:00:00 2001 From: Steven Ngesera Date: Fri, 10 Nov 2023 14:11:37 +0200 Subject: [PATCH] Replacing hm3.* by dotenv --- .env.dist | 68 +++++ composer.json | 3 +- composer.lock | 73 +++++- config/app.php | 476 ++++++++++++++++++++++++++++++++++ config/database.php | 67 +++++ index.php | 3 + lib/db.php | 34 +-- lib/environment.php | 105 ++++++++ lib/framework.php | 1 + tests/phpunit/environment.php | 50 ++++ tests/phpunit/phpunit.xml | 3 + 11 files changed, 864 insertions(+), 19 deletions(-) create mode 100644 .env.dist create mode 100644 config/app.php create mode 100644 config/database.php create mode 100644 lib/environment.php create mode 100644 tests/phpunit/environment.php diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000000..8c31a346f8 --- /dev/null +++ b/.env.dist @@ -0,0 +1,68 @@ +APP_NAME=Cypht + +DB_CONNECTION_TYPE=host +DB_DRIVER=mysql +DB_PORT=3306 +DB_HOST=localhost +DB_NAME=cypht_db +DB_USER=root +DB_PASS= +DB_SOCKET=/var/lib/mysqld/mysqld.sock + +SESSION_TYPE=PHP +AUTH_TYPE=DB +LDAP_AUTH_SERVER=localhost +LDAP_AUTH_PORT=389 +LDAP_AUTH_TLS= +LDAP_AUTH_BASE_DN=example,dc=com + +IMAP_AUTH_NAME=Gandi +IMAP_AUTH_SERVER=mail.gandi.net +IMAP_AUTH_PORT=993 +IMAP_AUTH_TLS=true +IMAP_AUTH_SIEVE_CONF_HOST=tls://mail.gandi.net:4190 + +DEFAULT_SMTP_NAME= +DEFAULT_SMTP_SERVER= +DEFAULT_SMTP_PORT= +DEFAULT_SMTP_TLS= +DEFAULT_SMTP_NO_AUTH= + +USER_CONFIG_TYPE=file +USER_SETTINGS_DIR=/Users/shadow243/Documents/var/lib/hm3/users +ATTACHMENT_DIR=/Users/shadow243/Documents/var/lib/hm3/attachments +APP_DATA_DIR=/Users/shadow243/Documents/var/lib/hm3/app_data + +ADMIN_USERS= + +COOKIE_DOMAIN= +COOKIE_PATH= + +DEFAULT_EMAIL_DOMAIN= +AUTO_CREATE_PROFILE= + +REDIRECT_AFTER_LOGIN= + +ALWAYS_MOBILE_UI= + +DEFAULT_LANGUAGE=en + +JS_COMPRESS=false +CSS_COMPRESS=false + +ALLOW_SESSION_CACHE=false +CACHE_CLASS= + +ENABLE_REDIS=true +REDIS_SERVER='127.0.0.1' +REDIS_PORT=6379 +REDIS_INDEX=1 +REDIS_PASS= +REDIS_SOCKET=/var/run/redis/redis-server.sock + +ENABLE_MEMCACHED=true +MEMCACHED_SERVER='127.0.0.1' +MEMCACHED_PORT=11211 +MEMCACHED_AUTH=false +MEMCACHED_USER= +MEMCACHED_PASS= \ No newline at end of file diff --git a/composer.json b/composer.json index f58cde8917..53dce9994c 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "webklex/composer-info": "^0.0.1", "composer" : "^2.0.0", "zbateson/mail-mime-parser": "^2.4", - "league/commonmark": "^2.4" + "league/commonmark": "^2.4", + "symfony/dotenv": "^4.3 || 5.4" }, "require-dev": { "phpunit/phpunit": "^9.3.0" diff --git a/composer.lock b/composer.lock index ab30c980a3..2121885dbf 100755 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a79c47279566fbf037e39c48cc66b210", + "content-hash": "27c38366440192e46c8346d1886f2d00", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1234,6 +1234,77 @@ ], "time": "2022-01-02T09:53:40+00:00" }, + { + "name": "symfony/dotenv", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "9bd173ff68fa90d39c59d91a42ae42b7f11713a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/9bd173ff68fa90d39c59d91a42ae42b7f11713a0", + "reference": "9bd173ff68fa90d39c59d91a42ae42b7f11713a0", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "require-dev": { + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "source": "https://github.com/symfony/dotenv/tree/v5.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-11-23T10:19:22+00:00" + }, { "name": "symfony/polyfill-iconv", "version": "v1.28.0", diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000000..a3d88c7fba --- /dev/null +++ b/config/app.php @@ -0,0 +1,476 @@ + env('SESSION_TYPE', 'PHP'), + + /* + | ------------------- + | Authentication Type + | ------------------- + | + | This setting defines how Cypht will authenticate your username and password + | when you login. If you want to use a database it must be correctly configured + | in the "DB Support" section and the hm_user table must be created. There are + | 3 PHP cli scripts to help manage database users in the scripts/ directory ( + | create_account.php, delete_account.php, and update_password.php). If you want + | to authenticate against an IMAP server, you must setup the imap_auth_* settings + | below. If you want to authenticate against an LDAP server, + | you must setup the ldap_auth_* settings. Finally, if you want to let users + | pick from a list of popular mail services or try to auto-discover a mail + | system, set this to dynamic and make sure the dynamic_login module set is + | enabled in the "Module Sets" section of this file. + | + | Valid values for this setting: + | + | DB Authenticate against the database + | LDAP Authenticate against an LDAP server + | IMAP Authenticate using an IMAP server + | dynamic Let the user choose from a list, or try to auto discover the mail + | services based on the email domain + | custom Create your own auth class. See the modules/site/lib.php file for + | more info + */ + 'auth_type' => env('AUTH_TYPE', 'DB'), + + /* + | ------------------- + | LDAP Authentication + | ------------------- + | + | If auth_type is set to LDAP, configure the LDAP server to authenticate against + | with the following settings, otherwise these are ignored. + | + | + | The hostname or IP address of the LDAP server to authenticate to + */ + 'ldap_auth_server' => env('LDAP_AUTH_SERVER', 'localhost'), + + /* + | + | The port the LDAP server is listening on. + | + */ + 'ldap_auth_port' => env('LDAP_AUTH_PORT', 389), + + /* + | + | Enable TLS/SSL connections. Leave blank or set to false to disable. Set to + | true to enable TLS connections. + | + */ + 'ldap_auth_tls' => env('LDAP_AUTH_TLS'), + + /* + | + | The "base dn" of the LDAP server + | + */ + 'ldap_auth_base_dn' => env('LDAP_AUTH_BASE_DN', 'example,dc=com'), + + /* + | ------------------- + | IMAP Authentication + | ------------------- + | + | If auth_type is set to IMAP, configure the IMAP server to authenticate against + | with the following settings, otherwise these are ignored. + | + | This is just a label used in the UI. It can be set to anything + */ + 'imap_auth_name' => env('IMAP_AUTH_NAME', 'Gandi'), + + /* + | + | The hostname or IP address of the IMAP server to authenticate to + | + */ + 'imap_auth_server' => env('IMAP_AUTH_SERVER', 'mail.gandi.net'), + + /* + | + | The hostname or IP address of the IMAP server to authenticate to + | + */ + 'imap_auth_port' => env('IMAP_AUTH_PORT', 993), + + /* + | + | Enable TLS/SSL connections. Leave blank or set to false to disable. Set to + | true to enable TLS connections. If you want to use IMAP STARTTLS, do NOT + | enable this. This is only for TLS enabled sockets (typically on port 993). + | + */ + 'imap_auth_tls' => env('IMAP_AUTH_TLS', true), + + /* + | + | The hostname/IP address and port sieve is listening on. Example: example.org:4190 + | Note: Add tls:// prefix to enable explicit STARTTLS + | + */ + 'imap_auth_sieve_conf_host' => env('IMAP_AUTH_SIEVE_CONF_HOST', 'tls://mail.gandi.net:4190'), + + /* + | ------------------- + | Default SMTP Server + | ------------------- + | + | You can set a default SMTP server for all Cypht users. Authentication will be + | done with the users login credentials, so this only makes sense if you are + | using IMAP for authentication. Leave these values blank to disable a + | default SMTP server, otherwise fill in the required values below + | + | This is just a label used in the UI. It can be set to anything + */ + 'default_smtp_name' => env('DEFAULT_SMTP_NAME'), + + /* + | + | The hostname or IP address of the SMTP server + | + */ + 'default_smtp_server' => env('DEFAULT_SMTP_SERVER'), + + /* + | + | The port the SMTP server is listening on. + | + */ + 'default_smtp_port' => env('DEFAULT_SMTP_PORT'), + + /* + | + | Enable TLS/SSL connections. Leave blank or set to false to disable. Set to + | true to enable TLS connections. + | + */ + 'default_smtp_tls' => env('DEFAULT_SMTP_TLS'), + + /* + | + | If your SMTP service does not require authentication, you can disable it + | by setting the following to true. + | + */ + 'default_smtp_no_auth' => env('DEFAULT_SMTP_NO_AUTH'), + + /* + | ---------------- + | Settings Storage + | ---------------- + | + | Cypht supports 3 methods for saving user settings between logins. File based + | settings, database table or custom implementation. To store settings in a + | database, it must be configured in the "DB Support" section and the + | hm_user_settings table must be created. To store settings on the filesystem, + | the user_settings_dir must be created and the webserver software must be able + | to write to it. For custom implementations, see Hm_User_Config_File. + | + | Valid values for this setting: + | file Store user settings in the filesystem + | DB Store user settings in a database + | custom Store user settings via custom implementation. Specify class name + | after colon, e.g. custom:Custom_User_Config + */ + + 'user_config_type' => env('USER_CONFIG_TYPE', 'file'), + + /* + | ----------------- + | Settings Location + | ----------------- + | + | If user_config_type is set to file, this must be set to an existing directory + | that the webserver software can read and write to. If settings storage is set + | to DB, this is ignored. It should not be inside the webserver document root. + | + */ + 'user_settings_dir' => env('USER_SETTINGS_DIR', '/var/lib/hm3/users'), + + /* + | ------------------- + | Attachment Location + | ------------------- + | + | If user_config_type is set to file, this must be set to an existing directory + | that the webserver software can read and write to. If settings storage is set + | to DB, this is ignored. It should not be inside the webserver document root. + | + */ + 'attachment_dir' => env('ATTACHMENT_DIR', '/var/lib/hm3/attachments'), + + /* + | ------------------------- + | Application Data Location + | ------------------------- + | + | Some Cypht module sets have their own ini files that need to be readable by + | the webserver software, but not writable, and definitely not inside the + | webserver document root. + | + */ + 'app_data_dir' => env('APP_DATA_DIR', '/var/lib/hm3/app_data'), + + /* + | -------------------- + | Disable origin check + | -------------------- + | + | To help protect against CSRF attacks, Cypht checks origin headers to confirm + | that the source and target origin domains match. If you are using proxies this + | could create a problem making it impossible to login. Change this to true to + | disable the origin check. + | + */ + 'disable_origin_check' => env('DISABLE_ORIGIN_CHECK', false), + + /* + | ----------- + | Admin Users + | ----------- + | + | You can define a comma delimited list of admin users that Cypht will grant + | special rights to. Currently this only enables the "create account" link in + | the account module set that provides a form to create a new account. This is + | only used if the auth_type is set to DB. Leave this blank if you don't want + | to define any admin users, or are using IMAP authentication. + | + */ + 'admin_users' => env('ADMIN_USERS'), + + /* + | ------------- + | Cookie Domain + | ------------- + | + | By default Cypht uses the server name used in the request to determine + | the domain name to set the cookie for. Configurations that use a reverse + | proxy might need to define the domain name used for cookies. Leave this + | blank to let Cypht automatically determine the domain. You can also use + | the special value of "none" to force Cypht to NOT set the cookie domain + | property at all. This is not recommended unless you know what you are + | doing! + | + */ + 'cookie_domain' => env('COOKIE_DOMAIN'), + + /* + | ----------- + | Cookie Path + | ----------- + | + | By default Cypht uses the request URI to determine the cookie path to set + | the cookie for. Configurations that use mod_rewrite might need to define + | the path used for cookies. E.g. /cypht/embedded?page=compose will set path + | to /cypht/embedded/ which won't send the cookies back to the server. In that + | case set cookie_path=/cypht/. Leave this blank to let Cypht automatically + | determine the path. You can also use the special value of "none" to force + | Cypht to NOT set the cookie path property at all. This is not recommended + | unless you know what you are doing! + | + */ + 'cookie_path' => env('COOKIE_PATH'), + + /* + | --------------------- + | Outbound Email Domain + | --------------------- + | + | Default domain used for outbound email addresses when using IMAP auth and + | users don't login with a full email address. Users can customize this with + | the profiles module which will override this default + | + */ + 'default_email_domain' => env('DEFAULT_EMAIL_DOMAIN'), + + /* + | ------------------- + | Auto-Create Profile + | ------------------- + | + | When a user logs in and they have only 1 IMAP server and 1 SMTP server, and + | no configured profiles - enabling this option will auto-create a profile for + | them. Email and reply-to addresses will use the default_email_domain if + | set, otherwise it will fallback to the domain Cypht is hosted on. + | + */ + 'autocreate_profile' => env('AUTO_CREATE_PROFILE'), + + /* + | -------------------- + | Redirect After Login + | -------------------- + | + | You can login directly to any page in Cypht by going to the correct url before + | logging in, but that is not very user-friendly. To redirect users to a url + | after login, add the url arguments below (everything in the url after, but + | including, the question mark). You must use double quotes around the value + | otherwise it will cause an ini parsing error. To redirect users after login + | to the combined unread view you would use: + | + | redirect_after_login="?page=message_list&list_path=unread" + | + */ + 'redirect_after_login' => env('REDIRECT_AFTER_LOGIN'), + + /* + | ---------------- + | Application Name + | ---------------- + | + | This label is used in the UI to reference the program - you can change it to + | "Your awesome webmail" to replace the Cypht name used in various places. + | + */ + 'app_name' => env('APP_NAME', 'Cypht'), + + /* + | --------------- + | Force Mobile UI + | --------------- + | + | Cypht will detect mobile devices and display a mobile optimized UI. If you want + | to aways use this UI regardless of device, set this to true + | + */ + 'always_mobile_ui' => env('ALWAYS_MOBILE_UI'), + + /* + | ---------------- + | Default Language + | ---------------- + | + | Users can select from available interface languages on the site settings page. + | This sets the default for a user who has not done so. Valid values are 2 character + | langauge codes that have matching language definitions in the language/ folder. + | + */ + 'default_language' => env('DEFAULT_LANGUAGE', 'en'), + + /* + | ---------------------- + | JavaScript Compression + | ---------------------- + | + | When the configuration script is run, all JavaScript files are concatenated + | and optionally compressed. To compress the content, define a command and its + | options below. Cypht does not come with compresson software, so you must + | install and configure that separately. Leave blank or set to false to disable + | external compression. Compression software must be able to handle ES6. + | Example: + | js_compress='uglifyjs.terser -c -m --verbose --warn' + | + */ + 'js_compress' => env('JS_COMPRESS', false), + + /* + | --------------- + | CSS Compression + | --------------- + | + | When the configuration script is run, all CSS files are concatenated and + | optionally compressed. To compress the content, define a command and its + | options below. Cypht does not come with compresson software, so you must + | install and configure that separately. Leave blank or set to false to disable + | external compression. + | + | Example: + | css_compress='java -jar /usr/local/lib/yuicompressor-2.4.8.jar --type css' + | + */ + 'css_compress' => env('CSS_COMPRESS', false), + + /* + | ---------------------- + | Caching Server Support + | ---------------------- + | + | Cypht can use Redis or Memcache to improve performance, as well as to store + | user sessions. Configure Redis or Memcached below and Cypht will + | automatically use them for caching. All data cached for a user in either + | system is encrypted. Currently, the feeds, and IMAP modules will use + | the configured cache. If both Redis and Memcached are configured, Redis will + | be used for the cache. + | + | If you want to use the user session as a cache, uncomment the line below and + | set to true. THIS IS NOT RECOMMENDED. Cypht uses parallel requests to the + | server, and using the session as a cache is likely to cause race conditions + | and integrity issues. If you are running Cypht in an "embedded" mode with + | only one email source, this option is less likely to be a problem. + | + | + | 'allow_session_cache' => env('ALLOW_SESSION_CACHE', false), + | 'cache_class' => env('CACHE_CLASS') + */ + + + /* + | ------------- + | Redis Support + | ------------- + | + | Configure Redis details below to use it for caching + */ + 'enable_redis' => env('ENABLE_REDIS', true), + + 'redis_server' => env('REDIS_SERVER', '127.0.0.1'), + + 'redis_port' => env('REDIS_PORT', 6379), + + 'redis_index' => env('REDIS_INDEX', 1), + + 'redis_pass' => env('REDIS_PASS'), + + 'redis_socket' => env('REDIS_SOCKET', '/var/run/redis/redis-server.sock'), + + /* + | ----------------- + | Memcached Support + | ----------------- + | + | Configure Memcached details below to use it for caching + */ + 'enable_memcached' => env('ENABLE_MEMCACHED',true), + + 'memcached_server' => env('MEMCACHED_SERVER','127.0.0.1'), + + 'memcached_port' => env('MEMCACHED_PORT',11211), + + /* + | + | If you need SASL authentication for memcached, set the following to true + | and add the username and password to authenticate with + | + */ + 'memcached_auth' => env('MEMCACHED_AUTH',false), + + 'memcached_user' => env('MEMCACHED_USER'), + + 'memcached_pass' => env('MEMCACHED_PASS') +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000000..c7ee21877a --- /dev/null +++ b/config/database.php @@ -0,0 +1,67 @@ + env('DB_CONNECTION_TYPE', 'host'), + + /* + | + | Database host name or ip address. If db_connection_type is set to "socket", + | this value is ignored + */ + 'db_host' => env('DB_HOST', '127.0.0.1'), + + /* + | + | Database port. Only needed if your database is running on a non-standard + | port + */ + 'db_port' => env('DB_PORT', 3306), + + /* + | + | If db_connection_type is set to "socket", this should be the filesystem + | location of the unix socket file. If db_connection_type is set to "host" + | this value is ignored. + */ + 'db_socket' => env('DB_SOCKET','/var/lib/mysqld/mysqld.sock'), + + /* + | + | Name of the database with the required tables + */ + 'db_name' => env('DB_NAME', 'cypht_db'), + + /* + | + | User to connect to the database with + */ + 'db_user' => env('DB_USER', 'root'), + + /* + | + | Password to connect to the database with + */ + 'db_pass' => env('DB_PASS'), + + /* + | + | Database type. can be any supported PDO driver ; (http://php.net/manual/en/pdo.drivers.php) + */ + 'db_driver' => env('DRIVER','mysql') +]; diff --git a/index.php b/index.php index 7c6f463cfe..a410c68fc1 100644 --- a/index.php +++ b/index.php @@ -25,6 +25,9 @@ if (DEBUG_MODE) { error_reporting(E_ALL | E_STRICT); } +/* //load env files */ +$environment = Hm_Environment::getInstance(); +$environment->load(); /* config file location */ define('CONFIG_FILE', APP_PATH.'hm3.rc'); diff --git a/lib/db.php b/lib/db.php index 863716a724..059a1a656d 100644 --- a/lib/db.php +++ b/lib/db.php @@ -26,7 +26,8 @@ class Hm_DB { * @return void */ static private function parse_config($site_config) { - self::$config = array('db_driver' => $site_config->get('db_driver', false), + self::$config = array( + 'db_driver' => $site_config->get('db_driver', false), 'db_host' => $site_config->get('db_host', false), 'db_name' => $site_config->get('db_name', false), 'db_user' => $site_config->get('db_user', false), @@ -35,6 +36,7 @@ static private function parse_config($site_config) { 'db_conn_type' => $site_config->get('db_connection_type', 'host'), 'db_port' => $site_config->get('db_port', false), ); + foreach (self::$required_config as $v) { if (!self::$config[$v]) { Hm_Debug::add(sprintf('Missing configuration setting for %s', $v)); @@ -47,14 +49,15 @@ static private function parse_config($site_config) { * @return string md5 of the DB settings */ static private function db_key() { - return md5(self::$config['db_driver']. - self::$config['db_host']. - self::$config['db_port']. - self::$config['db_name']. - self::$config['db_user']. - self::$config['db_pass']. - self::$config['db_conn_type']. - self::$config['db_socket'] + return md5( + self::$config['db_driver'] . + self::$config['db_host'] . + self::$config['db_port'] . + self::$config['db_name'] . + self::$config['db_user'] . + self::$config['db_pass'] . + self::$config['db_conn_type'] . + self::$config['db_socket'] ); } @@ -68,12 +71,10 @@ static public function build_dsn() { } if (self::$config['db_conn_type'] == 'socket') { return sprintf('%s:unix_socket=%s;dbname=%s', self::$config['db_driver'], self::$config['db_socket'], self::$config['db_name']); - } - else { + } else { if (self::$config['db_port']) { return sprintf('%s:host=%s;port=%s;dbname=%s', self::$config['db_driver'], self::$config['db_host'], self::$config['db_port'], self::$config['db_name']); - } - else { + } else { return sprintf('%s:host=%s;dbname=%s', self::$config['db_driver'], self::$config['db_host'], self::$config['db_name']); } } @@ -87,7 +88,7 @@ static public function build_dsn() { * @param bool $all optional flag to return multiple rows * @return boolean|integer|array */ - static public function execute($dbh, $sql, $args, $type=false, $all=false) { + static public function execute($dbh, $sql, $args, $type = false, $all = false) { if (!$dbh) { return false; } @@ -112,7 +113,7 @@ static public function execute($dbh, $sql, $args, $type=false, $all=false) { * @return string */ static private function execute_type($sql) { - switch(substr($sql, 0, 1)) { + switch (substr($sql, 0, 1)) { case 'd': case 'u': case 'i': @@ -141,8 +142,7 @@ static public function connect($site_config) { self::$dbh[$key]->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); Hm_Debug::add(sprintf('Connecting to dsn: %s', $dsn)); return self::$dbh[$key]; - } - catch (Exception $oops) { + } catch (Exception $oops) { Hm_Debug::add($oops->getMessage()); self::$dbh[$key] = false; return false; diff --git a/lib/environment.php b/lib/environment.php new file mode 100644 index 0000000000..8bb5d12126 --- /dev/null +++ b/lib/environment.php @@ -0,0 +1,105 @@ +set_required_environment_variables(); + + $dotenvLoader = new Dotenv(); + if (method_exists($dotenvLoader, 'usePutenv')) { + $dotenvLoader->usePutenv(true); + } + $envDistFile = APP_PATH. '.env.dist'; + if (!file_exists($envDistFile)) { + Hm_Msgs::add('ERR.env.dist file not found at: "' . $envDistFile . '"'); + return; + } + + $envFile = static::get('TM_DOTENV'); + $dotenvLoader->load($envDistFile); + if ($envFile) { + $dotenvLoader->loadEnv($envFile); + } + } + + public static function get($key, $defaultValue = null) + { + $variables = self::getInstance()->get_environment_variables(); + + return array_key_exists($key, $variables) ? $variables[$key] : $defaultValue; + } + + /** + * Sets required environment variables that are used within .env files + */ + private function set_required_environment_variables() + { + $_ENV['TM_DOTENV'] = APP_PATH . '.env'; + } + + /** + * Get a merge of environment variables $_ENV and $_SERVER. + * + * @return array + */ + protected function get_environment_variables() + { + return array_merge($_ENV, $_SERVER); + } +} + +if (! function_exists('config_env_file')) { + /** + * Get / set the specified configuration value. + * + * If an array is passed as the key, we will assume you want to set an array of values. + * + * @param array|string|null $key + * @param mixed $default + * @return mixed + */ + function config_env_file($key = null, $default = null) + { + if (is_null($key)) { + // TO DO + } + + if (is_array($key)) { + // TO DO + } + // TO DO + // return ConfigClass->get($key, $default); + } +} + +if (! function_exists('env')) { + /** + * Gets the value of an environment variable. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + function env($key, $default = null) + { + return getenv($key) ?: $default; + } +} diff --git a/lib/framework.php b/lib/framework.php index 26a854e3f5..df8886cce6 100644 --- a/lib/framework.php +++ b/lib/framework.php @@ -28,6 +28,7 @@ require APP_PATH.'lib/crypt.php'; require APP_PATH.'lib/crypt_sodium.php'; require APP_PATH.'lib/sodium_compat.php'; +require APP_PATH.'lib/environment.php'; require APP_PATH.'lib/db.php'; require APP_PATH.'lib/servers.php'; require APP_PATH.'lib/api.php'; diff --git a/tests/phpunit/environment.php b/tests/phpunit/environment.php new file mode 100644 index 0000000000..e107f81d83 --- /dev/null +++ b/tests/phpunit/environment.php @@ -0,0 +1,50 @@ +load(); + $tm_dotenv = $environment->get('TM_DOTENV'); + $this->assertStringEndsWith(".env", $tm_dotenv); + } + + /** + * @preserveGlobalState disabled + * @runInSeparateProcess + */ + public function test_get_default_value() { + $environment = Hm_Environment::getInstance(); + $environment->load(); + $undifined_env_data = $environment::get('APP_VERSION', "DEFAUL_VALUE"); + $this->assertEquals('DEFAUL_VALUE', $undifined_env_data); + } + + /** + * @preserveGlobalState disabled + * @runInSeparateProcess + */ + public function test_get_environment_variables() { + $environment = Hm_Environment::getInstance(); + $reflection = new ReflectionClass($environment); + $method = $reflection->getMethod('get_environment_variables'); + $method->setAccessible(true); + $env_vars = $method->invoke($environment); + $expected = array_merge($_ENV, $_SERVER); + $this->assertEquals($expected, $env_vars); + } +} +?> diff --git a/tests/phpunit/phpunit.xml b/tests/phpunit/phpunit.xml index 50a45a35ee..345cdb94db 100644 --- a/tests/phpunit/phpunit.xml +++ b/tests/phpunit/phpunit.xml @@ -23,6 +23,9 @@ ./config.php + + ./environment.php + ./db.php