From 3ef0af547479cf880a4840c92fa2ffe20132fd0e Mon Sep 17 00:00:00 2001 From: Tatevik Date: Fri, 24 Jul 2026 11:15:25 +0400 Subject: [PATCH 1/5] Refactor: env --- config/parameters.yml.dist | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config/parameters.yml.dist b/config/parameters.yml.dist index cf9a17e6..42807138 100644 --- a/config/parameters.yml.dist +++ b/config/parameters.yml.dist @@ -33,12 +33,13 @@ parameters: env(APP_POWERED_BY_PHPLIST): '0' app.preference_page_show_private_lists: '%%env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS)%%' env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS): '0' - app.rest_api_base_url: '%%env(REST_API_BASE_URL)%%' - env(REST_API_BASE_URL): 'http://api.phplist.local/api/v2' - api_base_url: '%%env(API_BASE_URL)%%' + + app.rest_api_base_url: '%%env(API_BASE_URL)%%/api/v2' + app.api_base_url: '%%env(API_BASE_URL)%%' env(API_BASE_URL): 'http://api.phplist.local/' app.frontend_base_url: '%%env(FRONT_END_BASE_URL)%%' env(FRONT_END_BASE_URL): 'http://frontend.phplist.local' + parallel_use_with_phplist3: '%%env(parallel_use_with_phplist3)%%' env(parallel_use_with_phplist3): '0' From 0c80945567d56c20efa10cae8e534510fcd1eac5 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Fri, 24 Jul 2026 13:19:57 +0400 Subject: [PATCH 2/5] feat: add support for .env configuration files and dotenv integration --- .env.dist | 95 ++++++++++++++++++ .gitignore | 4 +- CHANGELOG.md | 2 + README.md | 2 +- composer.json | 4 +- config/parameters.yml | 99 +++++++++++++++++++ config/parameters.yml.dist | 169 --------------------------------- src/Composer/ScriptHandler.php | 31 +++++- src/Core/Bootstrap.php | 20 +++- 9 files changed, 252 insertions(+), 174 deletions(-) create mode 100644 .env.dist create mode 100644 config/parameters.yml delete mode 100644 config/parameters.yml.dist diff --git a/.env.dist b/.env.dist new file mode 100644 index 00000000..fe611032 --- /dev/null +++ b/.env.dist @@ -0,0 +1,95 @@ +# This file is a "template" of what your .env file should look like. +# Set variables here that may be different on each deployment target of the app, +# e.g. development, staging, production. +# +# On `composer install`/`composer update`, this file is copied to `.env` (unless +# it already exists) and PHPLIST_SECRET is replaced with a freshly generated value. +# +# https://symfony.com/doc/current/configuration.html#configuring-environment-variables-in-env-files + +PHPLIST_DATABASE_DRIVER=pdo_mysql +PHPLIST_DATABASE_PATH= +PHPLIST_DATABASE_HOST=127.0.0.1 +PHPLIST_DATABASE_PORT=3306 +PHPLIST_DATABASE_NAME=phplistdb +PHPLIST_DATABASE_USER=phplist +PHPLIST_DATABASE_PASSWORD=phplist +DATABASE_PREFIX=phplist_ +LIST_TABLE_PREFIX=listattr_ + +APP_DEV_VERSION=0 +APP_DEV_EMAIL=dev@dev.com +APP_POWERED_BY_PHPLIST=0 +PREFERENCEPAGE_SHOW_PRIVATE_LISTS=0 + +API_BASE_URL=http://api.phplist.local/ +FRONT_END_BASE_URL=http://frontend.phplist.local + +parallel_use_with_phplist3=0 + +# Email configuration +MAILER_FROM=noreply@phplist.com +MAILER_DSN=null://null +CONFIRMATION_URL=http://api.phplist.local/api/v2/subscriber/confirm/ +SUBSCRIPTION_CONFIRMATION_URL=http://api.phplist.local/api/v2/subscription/confirm/ +PASSWORD_RESET_URL=https://example.com/reset/ +SHOW_UNSUBSCRIBELINK=1 + +# Bounce email settings +BOUNCE_EMAIL=bounce@phplist.com +BOUNCE_IMAP_PASS=bounce@phplist.com +BOUNCE_IMAP_HOST=imap.phplist.com +BOUNCE_IMAP_PORT=993 +BOUNCE_IMAP_ENCRYPTION=ssl +BOUNCE_IMAP_MAILBOX=/var/spool/mail/bounces +BOUNCE_IMAP_MAILBOX_NAME=INBOX,ONE_MORE +BOUNCE_IMAP_PROTOCOL=imap +BOUNCE_IMAP_UNSUBSCRIBE_THRESHOLD=5 +BOUNCE_IMAP_BLACKLIST_THRESHOLD=3 +BOUNCE_IMAP_PURGE=0 +BOUNCE_IMAP_PURGE_UNPROCESSED=0 + +# Messenger configuration for asynchronous processing +MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=true + +# A secret key that's used to generate certain security-related tokens +PHPLIST_SECRET=%s +VERIFY_SSL=1 + +APP_PHPLIST_ISP_CONF_PATH=/etc/phplist.conf + +# Message sending +MAILQUEUE_BATCH_SIZE=5 +MAILQUEUE_BATCH_PERIOD=5 +MAILQUEUE_THROTTLE=5 +MESSAGING_MAX_PROCESS_TIME=600 +MAX_MAILSIZE=209715200 +DEFAULT_MESSAGEAGE=691200 +USE_MANUAL_TEXT_PART=0 +MESSAGING_BLACKLIST_GRACE_TIME=600 +GOOGLE_SENDERID= +USE_AMAZONSES=0 +USE_PRECEDENCE_HEADER=0 +EMBEDEXTERNALIMAGES=0 +EMBEDUPLOADIMAGES=0 +EXTERNALIMAGE_MAXAGE=0 +EXTERNALIMAGE_TIMEOUT=30 +EXTERNALIMAGE_MAXSIZE=204800 +FORWARD_ALTERNATIVE_CONTENT=0 +EMAILTEXTCREDITS=0 +ALWAYS_ADD_USERTRACK=1 +SEND_LISTADMIN_COPY=0 + +FORWARD_EMAIL_PERIOD="1 minute" +FORWARD_EMAIL_COUNT=1 +FORWARD_PERSONAL_NOTE_SIZE=0 +FORWARD_FRIEND_COUNT_ATTRIBUTE= +KEEPFORWARDERATTRIBUTES=0 + +UPLOADIMAGES_DIR=uploadimages +PHPLIST_UPLOADS_MAX_SIZE=5M + +PUBLIC_SCHEMA=https +PHPLIST_ATTACHMENT_DOWNLOAD_URL=https://example.com/download/ +PHPLIST_ATTACHMENT_REPOSITORY_PATH=/tmp +MAX_AVATAR_SIZE=100000 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 25db886b..072e5252 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,9 @@ /composer.lock /config/bundles.yml /config/config_modules.yml -/config/parameters.yml +/.env +/.env.local +/.env.*.local /config/routing_modules.yml /nbproject /var/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0254484d..f6e2111f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Added - Graylog integration for centralized logging (#TBD) +- `symfony/dotenv` support: configuration values are now read from a `.env` file (generated from `.env.dist` on install/update), in addition to real environment variables (#TBD) ### Changed +- `config/parameters.yml.dist` no longer contains inline `env(VAR): default` fallbacks; defaults now live in `.env.dist` (#TBD) ### Deprecated diff --git a/README.md b/README.md index 2015718a..d82c8149 100755 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ this code. The phpList application is configured so that the built-in PHP web server can run in development and testing mode, while Apache can run in production mode. -Please first set the database credentials in `config/parameters.yml`. +Please first set the database credentials in `.env` (created from `.env.dist` on `composer install`/`composer update`). ### Development diff --git a/composer.json b/composer.json index 9c95fb23..691fbf37 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,8 @@ "ext-fileinfo": "*", "setasign/fpdf": "^1.8", "phpdocumentor/reflection-docblock": "^5.2", - "guzzlehttp/guzzle": "^7.4.5" + "guzzlehttp/guzzle": "^7.4.5", + "symfony/dotenv": "^6.4" }, "require-dev": { "phpunit/phpunit": "^9.5", @@ -127,6 +128,7 @@ "PhpList\\Core\\Composer\\ScriptHandler::createGeneralConfiguration", "PhpList\\Core\\Composer\\ScriptHandler::createBundleConfiguration", "PhpList\\Core\\Composer\\ScriptHandler::createRoutesConfiguration", + "PhpList\\Core\\Composer\\ScriptHandler::createDotenvConfiguration", "PhpList\\Core\\Composer\\ScriptHandler::createParametersConfiguration", "php bin/console cache:clear", "php bin/console cache:warmup" diff --git a/config/parameters.yml b/config/parameters.yml new file mode 100644 index 00000000..e387befc --- /dev/null +++ b/config/parameters.yml @@ -0,0 +1,99 @@ +# This file is a "template" of what your parameters.yml file should look like +# Set parameters here that may be different on each deployment target of the app, e.g. development, staging, production. +# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration +# +# These variables are read from environment variables using the "env" construct. +# The environment variables themselves are defined in the ".env" file (see ".env.dist" for the template) +# and/or in the actual environment (e.g. Apache host configuration, command line). +parameters: + database_driver: '%env(PHPLIST_DATABASE_DRIVER)%' + database_path: '%env(PHPLIST_DATABASE_PATH)%' + database_host: '%env(PHPLIST_DATABASE_HOST)%' + database_port: '%env(PHPLIST_DATABASE_PORT)%' + database_name: '%env(PHPLIST_DATABASE_NAME)%' + database_user: '%env(PHPLIST_DATABASE_USER)%' + database_password: '%env(PHPLIST_DATABASE_PASSWORD)%' + database_prefix: '%env(DATABASE_PREFIX)%' + list_table_prefix: '%env(LIST_TABLE_PREFIX)%' + app.dev_version: '%env(APP_DEV_VERSION)%' + app.dev_email: '%env(APP_DEV_EMAIL)%' + app.powered_by_phplist: '%env(APP_POWERED_BY_PHPLIST)%' + app.preference_page_show_private_lists: '%env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS)%' + + app.rest_api_base_url: '%env(API_BASE_URL)%/api/v2' + app.api_base_url: '%env(API_BASE_URL)%' + app.frontend_base_url: '%env(FRONT_END_BASE_URL)%' + + parallel_use_with_phplist3: '%env(parallel_use_with_phplist3)%' + + # Email configuration + app.mailer_from: '%env(MAILER_FROM)%' + app.mailer_dsn: '%env(MAILER_DSN)%' + app.confirmation_url: '%env(CONFIRMATION_URL)%' + app.subscription_confirmation_url: '%env(SUBSCRIPTION_CONFIRMATION_URL)%' + app.password_reset_url: '%env(PASSWORD_RESET_URL)%' + app.show_unsubscribe_link: '%env(SHOW_UNSUBSCRIBELINK)%' + + # bounce email settings + imap_bounce.email: '%env(BOUNCE_EMAIL)%' + imap_bounce.password: '%env(BOUNCE_IMAP_PASS)%' + imap_bounce.host: '%env(BOUNCE_IMAP_HOST)%' + imap_bounce.port: '%env(BOUNCE_IMAP_PORT)%' + imap_bounce.encryption: '%env(BOUNCE_IMAP_ENCRYPTION)%' + imap_bounce.mailbox: '%env(BOUNCE_IMAP_MAILBOX)%' + imap_bounce.mailbox_name: '%env(BOUNCE_IMAP_MAILBOX_NAME)%' + imap_bounce.protocol: '%env(BOUNCE_IMAP_PROTOCOL)%' + imap_bounce.unsubscribe_threshold: '%env(BOUNCE_IMAP_UNSUBSCRIBE_THRESHOLD)%' + imap_bounce.blacklist_threshold: '%env(BOUNCE_IMAP_BLACKLIST_THRESHOLD)%' + imap_bounce.purge: '%env(BOUNCE_IMAP_PURGE)%' + imap_bounce.purge_unprocessed: '%env(BOUNCE_IMAP_PURGE_UNPROCESSED)%' + + # Messenger configuration for asynchronous processing + app.messenger_transport_dsn: '%env(MESSENGER_TRANSPORT_DSN)%' + + # A secret key that's used to generate certain security-related tokens + secret: '%env(PHPLIST_SECRET)%' + phplist.verify_ssl: '%env(VERIFY_SSL)%' + + graylog_host: 'graylog.phplist.local' + graylog_port: 12201 + + app.phplist_isp_conf_path: '%env(APP_PHPLIST_ISP_CONF_PATH)%' + + # Message sending + messaging.mail_queue_batch_size: '%env(MAILQUEUE_BATCH_SIZE)%' + messaging.mail_queue_period: '%env(MAILQUEUE_BATCH_PERIOD)%' + messaging.mail_queue_throttle: '%env(MAILQUEUE_THROTTLE)%' + messaging.max_process_time: '%env(MESSAGING_MAX_PROCESS_TIME)%' + messaging.max_mail_size: '%env(MAX_MAILSIZE)%' + messaging.default_message_age: '%env(DEFAULT_MESSAGEAGE)%' + messaging.use_manual_text_part: '%env(USE_MANUAL_TEXT_PART)%' + messaging.blacklist_grace_time: '%env(MESSAGING_BLACKLIST_GRACE_TIME)%' + messaging.google_sender_id: '%env(GOOGLE_SENDERID)%' + messaging.use_amazon_ses: '%env(USE_AMAZONSES)%' + messaging.use_precedence_header: '%env(USE_PRECEDENCE_HEADER)%' + messaging.embed_external_images: '%env(EMBEDEXTERNALIMAGES)%' + messaging.embed_uploaded_images: '%env(EMBEDUPLOADIMAGES)%' + messaging.external_image_max_age: '%env(EXTERNALIMAGE_MAXAGE)%' + messaging.external_image_timeout: '%env(EXTERNALIMAGE_TIMEOUT)%' + messaging.external_image_max_size: '%env(EXTERNALIMAGE_MAXSIZE)%' + messaging.forward_alternative_content: '%env(FORWARD_ALTERNATIVE_CONTENT)%' + messaging.email_text_credits: '%env(EMAILTEXTCREDITS)%' + messaging.always_add_user_track: '%env(ALWAYS_ADD_USERTRACK)%' + messaging.send_list_admin_copy: '%env(SEND_LISTADMIN_COPY)%' + + phplist.forward_email_period: '%env(FORWARD_EMAIL_PERIOD)%' + phplist.forward_email_count: '%env(FORWARD_EMAIL_COUNT)%' + phplist.forward_personal_note_size: '%env(FORWARD_PERSONAL_NOTE_SIZE)%' + phplist.forward_friend_count_attribute: '%env(FORWARD_FRIEND_COUNT_ATTRIBUTE)%' + phplist.keep_forwarded_attributes: '%env(KEEPFORWARDERATTRIBUTES)%' + + phplist.upload_images_dir: '%env(UPLOADIMAGES_DIR)%' + phplist.uploads.allowed_mime_types: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'] + phplist.uploads.allowed_extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'] + phplist.uploads.max_size: '%env(PHPLIST_UPLOADS_MAX_SIZE)%' + + phplist.public_schema: '%env(PUBLIC_SCHEMA)%' + phplist.attachment_download_url: '%env(PHPLIST_ATTACHMENT_DOWNLOAD_URL)%' + phplist.attachment_repository_path: '%env(PHPLIST_ATTACHMENT_REPOSITORY_PATH)%' + phplist.max_avatar_size: '%env(MAX_AVATAR_SIZE)%' diff --git a/config/parameters.yml.dist b/config/parameters.yml.dist deleted file mode 100644 index 42807138..00000000 --- a/config/parameters.yml.dist +++ /dev/null @@ -1,169 +0,0 @@ -# This file is a "template" of what your parameters.yml file should look like -# Set parameters here that may be different on each deployment target of the app, e.g. development, staging, production. -# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration -# -# These variables are read from environment variables using the "env" construct. -# You can set environment variables in the Apache host configuration and also on the command line. -# If you cannot provide any environment variables, you can also set the variables in this file -# in the lines with "env(VARIABLE_NAME)". -parameters: - database_driver: '%%env(PHPLIST_DATABASE_DRIVER)%%' - env(PHPLIST_DATABASE_DRIVER): 'pdo_mysql' - database_path: '%%env(PHPLIST_DATABASE_PATH)%%' - env(PHPLIST_DATABASE_PATH): null - database_host: '%%env(PHPLIST_DATABASE_HOST)%%' - env(PHPLIST_DATABASE_HOST): '127.0.0.1' - database_port: '%%env(PHPLIST_DATABASE_PORT)%%' - env(PHPLIST_DATABASE_PORT): '3306' - database_name: '%%env(PHPLIST_DATABASE_NAME)%%' - env(PHPLIST_DATABASE_NAME): 'phplistdb' - database_user: '%%env(PHPLIST_DATABASE_USER)%%' - env(PHPLIST_DATABASE_USER): 'phplist' - database_password: '%%env(PHPLIST_DATABASE_PASSWORD)%%' - env(PHPLIST_DATABASE_PASSWORD): 'phplist' - database_prefix: '%%env(DATABASE_PREFIX)%%' - env(DATABASE_PREFIX): 'phplist_' - list_table_prefix: '%%env(LIST_TABLE_PREFIX)%%' - env(LIST_TABLE_PREFIX): 'listattr_' - app.dev_version: '%%env(APP_DEV_VERSION)%%' - env(APP_DEV_VERSION): '0' - app.dev_email: '%%env(APP_DEV_EMAIL)%%' - env(APP_DEV_EMAIL): 'dev@dev.com' - app.powered_by_phplist: '%%env(APP_POWERED_BY_PHPLIST)%%' - env(APP_POWERED_BY_PHPLIST): '0' - app.preference_page_show_private_lists: '%%env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS)%%' - env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS): '0' - - app.rest_api_base_url: '%%env(API_BASE_URL)%%/api/v2' - app.api_base_url: '%%env(API_BASE_URL)%%' - env(API_BASE_URL): 'http://api.phplist.local/' - app.frontend_base_url: '%%env(FRONT_END_BASE_URL)%%' - env(FRONT_END_BASE_URL): 'http://frontend.phplist.local' - - parallel_use_with_phplist3: '%%env(parallel_use_with_phplist3)%%' - env(parallel_use_with_phplist3): '0' - - # Email configuration - app.mailer_from: '%%env(MAILER_FROM)%%' - env(MAILER_FROM): 'noreply@phplist.com' - app.mailer_dsn: '%%env(MAILER_DSN)%%' - env(MAILER_DSN): 'null://null' # set local_domain on transport - app.confirmation_url: '%%env(CONFIRMATION_URL)%%' - env(CONFIRMATION_URL): 'http://api.phplist.local/api/v2/subscriber/confirm/' - app.subscription_confirmation_url: '%%env(SUBSCRIPTION_CONFIRMATION_URL)%%' - env(SUBSCRIPTION_CONFIRMATION_URL): 'http://api.phplist.local/api/v2/subscription/confirm/' - app.password_reset_url: '%%env(PASSWORD_RESET_URL)%%' - env(PASSWORD_RESET_URL): 'https://example.com/reset/' - app.show_unsubscribe_link: '%%env(SHOW_UNSUBSCRIBELINK)%%' - env(SHOW_UNSUBSCRIBELINK): '1' - - # bounce email settings - imap_bounce.email: '%%env(BOUNCE_EMAIL)%%' - env(BOUNCE_EMAIL): 'bounce@phplist.com' - imap_bounce.password: '%%env(BOUNCE_IMAP_PASS)%%' - env(BOUNCE_IMAP_PASS): 'bounce@phplist.com' - imap_bounce.host: '%%env(BOUNCE_IMAP_HOST)%%' - env(BOUNCE_IMAP_HOST): 'imap.phplist.com' - imap_bounce.port: '%%env(BOUNCE_IMAP_PORT)%%' - env(BOUNCE_IMAP_PORT): '993' - imap_bounce.encryption: '%%env(BOUNCE_IMAP_ENCRYPTION)%%' - env(BOUNCE_IMAP_ENCRYPTION): 'ssl' - imap_bounce.mailbox: '%%env(BOUNCE_IMAP_MAILBOX)%%' - env(BOUNCE_IMAP_MAILBOX): '/var/spool/mail/bounces' - imap_bounce.mailbox_name: '%%env(BOUNCE_IMAP_MAILBOX_NAME)%%' - env(BOUNCE_IMAP_MAILBOX_NAME): 'INBOX,ONE_MORE' - imap_bounce.protocol: '%%env(BOUNCE_IMAP_PROTOCOL)%%' - env(BOUNCE_IMAP_PROTOCOL): 'imap' - imap_bounce.unsubscribe_threshold: '%%env(BOUNCE_IMAP_UNSUBSCRIBE_THRESHOLD)%%' - env(BOUNCE_IMAP_UNSUBSCRIBE_THRESHOLD): '5' - imap_bounce.blacklist_threshold: '%%env(BOUNCE_IMAP_BLACKLIST_THRESHOLD)%%' - env(BOUNCE_IMAP_BLACKLIST_THRESHOLD): '3' - imap_bounce.purge: '%%env(BOUNCE_IMAP_PURGE)%%' - env(BOUNCE_IMAP_PURGE): '0' - imap_bounce.purge_unprocessed: '%%env(BOUNCE_IMAP_PURGE_UNPROCESSED)%%' - env(BOUNCE_IMAP_PURGE_UNPROCESSED): '0' - - # Messenger configuration for asynchronous processing - app.messenger_transport_dsn: '%%env(MESSENGER_TRANSPORT_DSN)%%' - env(MESSENGER_TRANSPORT_DSN): 'doctrine://default?auto_setup=true' - - # A secret key that's used to generate certain security-related tokens - secret: '%%env(PHPLIST_SECRET)%%' - env(PHPLIST_SECRET): %1$s - phplist.verify_ssl: '%%env(VERIFY_SSL)%%' - env(VERIFY_SSL): '1' - - graylog_host: 'graylog.phplist.local' - graylog_port: 12201 - - app.phplist_isp_conf_path: '%%env(APP_PHPLIST_ISP_CONF_PATH)%%' - env(APP_PHPLIST_ISP_CONF_PATH): '/etc/phplist.conf' - - # Message sending - messaging.mail_queue_batch_size: '%%env(MAILQUEUE_BATCH_SIZE)%%' - env(MAILQUEUE_BATCH_SIZE): '5' - messaging.mail_queue_period: '%%env(MAILQUEUE_BATCH_PERIOD)%%' - env(MAILQUEUE_BATCH_PERIOD): '5' - messaging.mail_queue_throttle: '%%env(MAILQUEUE_THROTTLE)%%' - env(MAILQUEUE_THROTTLE): '5' - messaging.max_process_time: '%%env(MESSAGING_MAX_PROCESS_TIME)%%' - env(MESSAGING_MAX_PROCESS_TIME): '600' - messaging.max_mail_size: '%%env(MAX_MAILSIZE)%%' - env(MAX_MAILSIZE): '209715200' - messaging.default_message_age: '%%env(DEFAULT_MESSAGEAGE)%%' - env(DEFAULT_MESSAGEAGE): '691200' - messaging.use_manual_text_part: '%%env(USE_MANUAL_TEXT_PART)%%' - env(USE_MANUAL_TEXT_PART): '0' - messaging.blacklist_grace_time: '%%env(MESSAGING_BLACKLIST_GRACE_TIME)%%' - env(MESSAGING_BLACKLIST_GRACE_TIME): '600' - messaging.google_sender_id: '%%env(GOOGLE_SENDERID)%%' - env(GOOGLE_SENDERID): '' - messaging.use_amazon_ses: '%%env(USE_AMAZONSES)%%' - env(USE_AMAZONSES): '0' - messaging.use_precedence_header: '%%env(USE_PRECEDENCE_HEADER)%%' - env(USE_PRECEDENCE_HEADER): '0' - messaging.embed_external_images: '%%env(EMBEDEXTERNALIMAGES)%%' - env(EMBEDEXTERNALIMAGES): '0' - messaging.embed_uploaded_images: '%%env(EMBEDUPLOADIMAGES)%%' - env(EMBEDUPLOADIMAGES): '0' - messaging.external_image_max_age: '%%env(EXTERNALIMAGE_MAXAGE)%%' - env(EXTERNALIMAGE_MAXAGE): '0' - messaging.external_image_timeout: '%%env(EXTERNALIMAGE_TIMEOUT)%%' - env(EXTERNALIMAGE_TIMEOUT): '30' - messaging.external_image_max_size: '%%env(EXTERNALIMAGE_MAXSIZE)%%' - env(EXTERNALIMAGE_MAXSIZE): '204800' - messaging.forward_alternative_content: '%%env(FORWARD_ALTERNATIVE_CONTENT)%%' - env(FORWARD_ALTERNATIVE_CONTENT): '0' - messaging.email_text_credits: '%%env(EMAILTEXTCREDITS)%%' - env(EMAILTEXTCREDITS): '0' - messaging.always_add_user_track: '%%env(ALWAYS_ADD_USERTRACK)%%' - env(ALWAYS_ADD_USERTRACK): '1' - messaging.send_list_admin_copy: '%%env(SEND_LISTADMIN_COPY)%%' - env(SEND_LISTADMIN_COPY): '0' - - phplist.forward_email_period: '%%env(FORWARD_EMAIL_PERIOD)%%' - env(FORWARD_EMAIL_PERIOD): '1 minute' - phplist.forward_email_count: '%%env(FORWARD_EMAIL_COUNT)%%' - env(FORWARD_EMAIL_COUNT): '1' - phplist.forward_personal_note_size: '%%env(FORWARD_PERSONAL_NOTE_SIZE)%%' - env(FORWARD_PERSONAL_NOTE_SIZE): '0' - phplist.forward_friend_count_attribute: '%%env(FORWARD_FRIEND_COUNT_ATTRIBUTE)%%' - env(FORWARD_FRIEND_COUNT_ATTRIBUTE): '' - phplist.keep_forwarded_attributes: '%%env(KEEPFORWARDERATTRIBUTES)%%' - env(KEEPFORWARDERATTRIBUTES): '0' - - phplist.upload_images_dir: '%%env(UPLOADIMAGES_DIR)%%' - env(UPLOADIMAGES_DIR): 'uploadimages' - phplist.uploads.allowed_mime_types: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'] - phplist.uploads.allowed_extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'] - phplist.uploads.max_size: '%%env(PHPLIST_UPLOADS_MAX_SIZE)%%' - env(PHPLIST_UPLOADS_MAX_SIZE): '5M' - - phplist.public_schema: '%%env(PUBLIC_SCHEMA)%%' - env(PUBLIC_SCHEMA): 'https' - phplist.attachment_download_url: '%%env(PHPLIST_ATTACHMENT_DOWNLOAD_URL)%%' - env(PHPLIST_ATTACHMENT_DOWNLOAD_URL): 'https://example.com/download/' - phplist.attachment_repository_path: '%%env(PHPLIST_ATTACHMENT_REPOSITORY_PATH)%%' - env(PHPLIST_ATTACHMENT_REPOSITORY_PATH): '/tmp' - phplist.max_avatar_size: '%%env(MAX_AVATAR_SIZE)%%' - env(MAX_AVATAR_SIZE): '100000' diff --git a/src/Composer/ScriptHandler.php b/src/Composer/ScriptHandler.php index 55e23739..867e2fac 100644 --- a/src/Composer/ScriptHandler.php +++ b/src/Composer/ScriptHandler.php @@ -48,6 +48,16 @@ class ScriptHandler */ const PARAMETERS_TEMPLATE_FILE = '/config/parameters.yml.dist'; + /** + * @var string + */ + const DOTENV_FILE = '/.env'; + + /** + * @var string + */ + const DOTENV_TEMPLATE_FILE = '/.env.dist'; + /** * @return string absolute application root directory without the trailing slash * @@ -277,12 +287,31 @@ public static function createParametersConfiguration(): void } $templateFilePath = __DIR__ . '/../..' . static::PARAMETERS_TEMPLATE_FILE; + $configuration = file_get_contents($templateFilePath); + + self::createAndWriteFile($configurationFilePath, $configuration); + } + + /** + * Creates the .env file (the environment variables consumed by the parameters configuration) + * by copying it from .env.dist, generating a fresh app secret in the process. + * + * @return void + */ + public static function createDotenvConfiguration(): void + { + $dotenvFilePath = self::getApplicationRoot() . self::DOTENV_FILE; + if (file_exists($dotenvFilePath)) { + return; + } + + $templateFilePath = __DIR__ . '/../..' . static::DOTENV_TEMPLATE_FILE; $template = file_get_contents($templateFilePath); $secret = bin2hex(random_bytes(20)); $configuration = sprintf($template, $secret); - self::createAndWriteFile($configurationFilePath, $configuration); + self::createAndWriteFile($dotenvFilePath, $configuration); } /** diff --git a/src/Core/Bootstrap.php b/src/Core/Bootstrap.php index 82ddb28f..3b7430c2 100644 --- a/src/Core/Bootstrap.php +++ b/src/Core/Bootstrap.php @@ -7,6 +7,7 @@ use Doctrine\ORM\EntityManagerInterface; use Exception; use RuntimeException; +use Symfony\Component\Dotenv\Dotenv; use Symfony\Component\ErrorHandler\ErrorHandler; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -147,10 +148,27 @@ public function configure(): Bootstrap { $this->isConfigured = true; - return $this->configureDebugging() + return $this->loadEnvironmentVariables() + ->configureDebugging() ->configureApplicationKernel(); } + /** + * Loads environment variables from the application's ".env" files (if present) using Symfony Dotenv, + * following the standard ".env" -> ".env.local" -> ".env.$environment" -> ".env.$environment.local" cascade. + * + * @return Bootstrap fluent interface + */ + private function loadEnvironmentVariables(): Bootstrap + { + $applicationRoot = $this->applicationStructure->getApplicationRoot(); + if (file_exists($applicationRoot . '/.env') || file_exists($applicationRoot . '/.env.dist')) { + (new Dotenv())->loadEnv($applicationRoot . '/.env', 'APP_ENV', $this->environment); + } + + return $this; + } + /** * Makes sure that configure has been called before. * From d0c2f619ef4122e86fb1dd313aa5fbd6e85d4bf6 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 26 Jul 2026 11:28:58 +0400 Subject: [PATCH 3/5] feat: add parameters configuration file creation to ScriptHandler --- composer.json | 1 - src/Composer/ScriptHandler.php | 49 ++++++++++++++++------------------ 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index 691fbf37..2378bdeb 100644 --- a/composer.json +++ b/composer.json @@ -129,7 +129,6 @@ "PhpList\\Core\\Composer\\ScriptHandler::createBundleConfiguration", "PhpList\\Core\\Composer\\ScriptHandler::createRoutesConfiguration", "PhpList\\Core\\Composer\\ScriptHandler::createDotenvConfiguration", - "PhpList\\Core\\Composer\\ScriptHandler::createParametersConfiguration", "php bin/console cache:clear", "php bin/console cache:warmup" ], diff --git a/src/Composer/ScriptHandler.php b/src/Composer/ScriptHandler.php index 867e2fac..9b3ff57c 100644 --- a/src/Composer/ScriptHandler.php +++ b/src/Composer/ScriptHandler.php @@ -33,11 +33,6 @@ class ScriptHandler */ const ROUTES_CONFIGURATION_FILE = '/config/routing_modules.yml'; - /** - * @var string - */ - const PARAMETERS_CONFIGURATION_FILE = '/config/parameters.yml'; - /** * @var string */ @@ -46,17 +41,17 @@ class ScriptHandler /** * @var string */ - const PARAMETERS_TEMPLATE_FILE = '/config/parameters.yml.dist'; + const DOTENV_FILE = '/.env'; /** * @var string */ - const DOTENV_FILE = '/.env'; + const DOTENV_TEMPLATE_FILE = '/.env.dist'; /** * @var string */ - const DOTENV_TEMPLATE_FILE = '/.env.dist'; + const PARAMETERS_CONFIGURATION_FILE = '/config/parameters.yml'; /** * @return string absolute application root directory without the trailing slash @@ -275,43 +270,45 @@ public static function clearAllCaches():void } /** - * Creates config/parameters.yml (the parameters configuration file). + * Creates the .env file (the environment variables consumed by the parameters configuration) + * by copying it from .env.dist, generating a fresh app secret in the process. * * @return void */ - public static function createParametersConfiguration(): void + public static function createDotenvConfiguration(): void { - $configurationFilePath = self::getApplicationRoot() . self::PARAMETERS_CONFIGURATION_FILE; - if (file_exists($configurationFilePath)) { + $appDotenvFilePath = self::getApplicationRoot() . self::DOTENV_FILE; + $templateFilePath = __DIR__ . '/../..' . static::DOTENV_TEMPLATE_FILE; + + if (file_exists($appDotenvFilePath)) { return; } - $templateFilePath = __DIR__ . '/../..' . static::PARAMETERS_TEMPLATE_FILE; - $configuration = file_get_contents($templateFilePath); + $template = file_get_contents($templateFilePath); - self::createAndWriteFile($configurationFilePath, $configuration); + $secret = bin2hex(random_bytes(20)); + $configuration = sprintf($template, $secret); + + self::createAndWriteFile($appDotenvFilePath, $configuration); } + /** - * Creates the .env file (the environment variables consumed by the parameters configuration) - * by copying it from .env.dist, generating a fresh app secret in the process. + * Creates config/parameters.yml (the parameters configuration file). * * @return void */ - public static function createDotenvConfiguration(): void + public static function createParametersConfiguration(): void { - $dotenvFilePath = self::getApplicationRoot() . self::DOTENV_FILE; - if (file_exists($dotenvFilePath)) { + $configurationFilePath = self::getApplicationRoot() . self::PARAMETERS_CONFIGURATION_FILE; + if (file_exists($configurationFilePath)) { return; } - $templateFilePath = __DIR__ . '/../..' . static::DOTENV_TEMPLATE_FILE; - $template = file_get_contents($templateFilePath); - - $secret = bin2hex(random_bytes(20)); - $configuration = sprintf($template, $secret); + $templateFilePath = __DIR__ . '/../..' . static::PARAMETERS_CONFIGURATION_FILE; + $configuration = file_get_contents($templateFilePath); - self::createAndWriteFile($dotenvFilePath, $configuration); + self::createAndWriteFile($configurationFilePath, $configuration); } /** From 1e8a554602c97c9f413a695d1519e6e4ee2c2a6e Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 26 Jul 2026 12:35:01 +0400 Subject: [PATCH 4/5] remove legacy public app files --- public/app.php | 11 ----------- public/app_dev.php | 14 -------------- public/app_test.php | 14 -------------- 3 files changed, 39 deletions(-) delete mode 100644 public/app.php delete mode 100644 public/app_dev.php delete mode 100644 public/app_test.php diff --git a/public/app.php b/public/app.php deleted file mode 100644 index 8e58c4f4..00000000 --- a/public/app.php +++ /dev/null @@ -1,11 +0,0 @@ -configure() - ->dispatch(); diff --git a/public/app_dev.php b/public/app_dev.php deleted file mode 100644 index 46c49194..00000000 --- a/public/app_dev.php +++ /dev/null @@ -1,14 +0,0 @@ -ensureDevelopmentOrTestingEnvironment() - ->setEnvironment(Environment::DEVELOPMENT) - ->configure() - ->dispatch(); diff --git a/public/app_test.php b/public/app_test.php deleted file mode 100644 index af816b87..00000000 --- a/public/app_test.php +++ /dev/null @@ -1,14 +0,0 @@ -ensureDevelopmentOrTestingEnvironment() - ->setEnvironment(Environment::TESTING) - ->configure() - ->dispatch(); From f078d2fbc5dd4b7a4eeacf8dc8cc1f3ec8e44138 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 26 Jul 2026 12:46:07 +0400 Subject: [PATCH 5/5] fix: correct environment variable naming for parallel usage with phplist3 --- .env.dist | 4 ++-- config/parameters.yml | 2 +- src/Composer/ScriptHandler.php | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.env.dist b/.env.dist index fe611032..27298ab0 100644 --- a/.env.dist +++ b/.env.dist @@ -25,7 +25,7 @@ PREFERENCEPAGE_SHOW_PRIVATE_LISTS=0 API_BASE_URL=http://api.phplist.local/ FRONT_END_BASE_URL=http://frontend.phplist.local -parallel_use_with_phplist3=0 +PARALLER_USE_WITH_PHPLIST3=0 # Email configuration MAILER_FROM=noreply@phplist.com @@ -92,4 +92,4 @@ PHPLIST_UPLOADS_MAX_SIZE=5M PUBLIC_SCHEMA=https PHPLIST_ATTACHMENT_DOWNLOAD_URL=https://example.com/download/ PHPLIST_ATTACHMENT_REPOSITORY_PATH=/tmp -MAX_AVATAR_SIZE=100000 \ No newline at end of file +MAX_AVATAR_SIZE=100000 diff --git a/config/parameters.yml b/config/parameters.yml index e387befc..aecc30ec 100644 --- a/config/parameters.yml +++ b/config/parameters.yml @@ -24,7 +24,7 @@ parameters: app.api_base_url: '%env(API_BASE_URL)%' app.frontend_base_url: '%env(FRONT_END_BASE_URL)%' - parallel_use_with_phplist3: '%env(parallel_use_with_phplist3)%' + parallel_use_with_phplist3: '%env(PARALLER_USE_WITH_PHPLIST3)%' # Email configuration app.mailer_from: '%env(MAILER_FROM)%' diff --git a/src/Composer/ScriptHandler.php b/src/Composer/ScriptHandler.php index 9b3ff57c..426ac71c 100644 --- a/src/Composer/ScriptHandler.php +++ b/src/Composer/ScriptHandler.php @@ -301,10 +301,6 @@ public static function createDotenvConfiguration(): void public static function createParametersConfiguration(): void { $configurationFilePath = self::getApplicationRoot() . self::PARAMETERS_CONFIGURATION_FILE; - if (file_exists($configurationFilePath)) { - return; - } - $templateFilePath = __DIR__ . '/../..' . static::PARAMETERS_CONFIGURATION_FILE; $configuration = file_get_contents($templateFilePath);