Feature flag management system with RESTful API and web admin interface
Phlag lets you control feature rollouts and configuration values across your applications with temporal scheduling and type-safe values. Built with PHP 8.4+, it provides both a web UI for management and APIs for flag consumption.
- 🎯 Typed Flags: SWITCH (boolean), INTEGER, FLOAT, STRING
- ⏰ Temporal Control: Schedule flags with start/end dates
- 🌐 Web Interface: Clean admin UI for managing flags, API keys, and users
- 🔑 Auto-generated API Keys: 64-character cryptographically secure keys
- 📧 Password Reset: Email-based password recovery
- 🗄️ Multi-Database: MySQL, PostgreSQL, SQLite support
- 📦 Client Libraries: Official JavaScript and PHP clients available
- PHP 8.4 or higher
- Composer
- One of: MySQL 5.7+, PostgreSQL 9.6+, or SQLite 3
- Web server (Apache, Nginx, or PHP built-in server)
- (Optional) SMTP server for password reset emails
- Install via Composer
composer create-project moonspot/phlag
cd phlag- Set up the database
Choose your database and run the appropriate schema:
# MySQL
mysql -u root -p your_database < schema/mysql.sql
# PostgreSQL
psql -U postgres -d your_database -f schema/pgsql.sql
# SQLite
sqlite3 phlag.db < schema/sqlite.sql- Configure database connection
Create etc/config.ini from the example:
[db]
db.phlag.type = mysql
db.phlag.server = localhost
db.phlag.port = 3306
db.phlag.db = phlag
db.phlag.user = phlag_user
db.phlag.pass = your_secure_passwordFor PostgreSQL, use type = pgsql. For SQLite, use type = sqlite and set server to the path of your .db file.
Optional: Configure base URL path
If Phlag is installed in a subdirectory (e.g., https://example.com/phlag), add to etc/config.ini:
[phlag]
phlag.base_url_path = /phlagThis ensures API responses generate correct resource URLs. Omit this setting if Phlag is at the domain root.
- Configure email (optional, for password reset)
Add to etc/config.ini:
[mailer]
mailer.from.address = [email protected]
mailer.method = smtp
mailer.smtp.host = smtp.example.com
mailer.smtp.port = 587
mailer.smtp.encryption = tls
mailer.smtp.username = your-smtp-username
mailer.smtp.password = your-smtp-passwordSee etc/config.ini.example for detailed email configuration options including Gmail, SendGrid, and Mailgun examples.
- Start the application
For development, use PHP's built-in server:
php -S localhost:8000 -t publicFor production, configure your web server to serve public/ as the document root.
- Create your first user
Navigate to http://localhost:8000/first-user and create an admin account. This page only appears when no users exist.
- Start managing flags!
Log in at http://localhost:8000/login and you're ready to create feature flags.
A pre-built Docker image is available at Docker Hub:
# Pull the image
docker pull brianlmoon/phlag
# Run with MySQL (recommended for production)
docker run -d -p 8000:80 \
-e DB_PHLAG_TYPE=mysql \
-e DB_PHLAG_HOST=your-mysql-host \
-e DB_PHLAG_PORT=3306 \
-e DB_PHLAG_DB=phlag \
-e DB_PHLAG_USER=phlag_user \
-e DB_PHLAG_PASS=your_password \
brianlmoon/phlagVisit http://localhost:8000/first-user to create your initial admin user.
Create a .htaccess file in public/:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]server {
listen 80;
server_name phlag.example.com;
root /path/to/phlag/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}See QUICKSTART for a detailed tutorial of getting started using the application.
Official client libraries are available to simplify integration with Phlag:
- Repository: phlag-js-client
- Use Cases: Node.js services
- Features: Promise-based API, TypeScript support, automatic type casting
import PhlagClient from 'phlag-js-client';
const client = new PhlagClient({
baseUrl: 'http://localhost:8000',
apiKey: 'your-api-key'
});
const isEnabled = await client.getFlag('feature_checkout');- Repository: phlag-php-client
- Use Cases: PHP applications, backend services
- Features: Type-safe responses, PSR-compliant, Composer integration
use Phlag\Client\PhlagClient;
$client = new PhlagClient(
'http://localhost:8000',
'your-api-key'
);
$isEnabled = $client->getFlag('feature_checkout');For other languages or custom integrations, use the Flag API endpoints directly (see below).
-
Create a flag: Navigate to "Flags" → "Create New Flag"
- Name: Alphanumeric with underscores/hyphens (e.g.,
feature_checkout) - Type: SWITCH, INTEGER, FLOAT, or STRING
- Value: Type-appropriate value
- Optional: Set start/end dates for temporal control
- Name: Alphanumeric with underscores/hyphens (e.g.,
-
Create an API key: Navigate to "API Keys" → "Create New API Key"
- Enter description (e.g., "Production Web App")
- Copy the 64-character key (shown once only!)
-
Add users: Navigate to "Users" → "Create New User"
- Provide username, full name, email, password
Phlag provides three endpoints for retrieving flag values. All require Bearer token authentication.
Returns the current evaluated value as a scalar:
curl -H "Authorization: Bearer your-api-key" \
http://localhost:8000/flag/feature_checkoutResponse examples:
true # SWITCH flag (active)
false # SWITCH flag (inactive)
100 # INTEGER flag
3.14 # FLOAT flag
"welcome message" # STRING flag
null # Inactive or non-existent flagReturns all flags as a key-value object:
curl -H "Authorization: Bearer your-api-key" \
http://localhost:8000/all-flagsResponse:
{
"feature_checkout": true,
"max_items": 100,
"price_multiplier": 1.5,
"welcome_message": "Hello World"
}Returns complete flag details including temporal constraints:
curl -H "Authorization: Bearer your-api-key" \
http://localhost:8000/get-flagsResponse:
[
{
"name": "feature_checkout",
"type": "SWITCH",
"value": true,
"start_datetime": null,
"end_datetime": null
},
{
"name": "holiday_promo",
"type": "SWITCH",
"value": true,
"start_datetime": "2025-12-01T00:00:00+00:00",
"end_datetime": "2025-12-31T23:59:59+00:00"
}
]Flags can be scheduled to activate/deactivate automatically:
- Start datetime: Flag becomes active at this time
- End datetime: Flag becomes inactive after this time
- Both are optional (null = no constraint)
Behavior when inactive:
- SWITCH flags return
false - INTEGER/FLOAT/STRING flags return
null
phlag/
├── etc/
│ └── config.ini # Database and email configuration
├── public/
│ ├── index.php # Application entry point
│ └── assets/ # CSS, JavaScript, images
├── schema/
│ ├── mysql.sql # MySQL schema
│ ├── pgsql.sql # PostgreSQL schema
│ └── sqlite.sql # SQLite schema
├── src/
│ ├── Action/ # Custom API endpoints
│ ├── Data/ # Value objects (Phlag, PhlagApiKey, PhlagUser)
│ ├── Mapper/ # Data mappers with auto-features
│ └── Web/ # Controllers, templates, security
├── tests/ # PHPUnit tests
└── vendor/ # Composer dependencies
- CSRF Protection: Token-based protection on login and user creation forms
- Password Security: Bcrypt hashing with cost factor 12
- API Key Generation: Cryptographically secure random_bytes()
- Session Security: ID regeneration, timeout tracking, destruction on logout
- XSS Prevention: Twig auto-escaping, manual escaping in JavaScript
- Input Validation: Type checking, pattern matching, length constraints
# Run all tests
./vendor/bin/phpunit
# Run with detailed output
./vendor/bin/phpunit --testdox
# Run specific test file
./vendor/bin/phpunit tests/Unit/Action/GetPhlagStateTest.php
# Run specific test
./vendor/bin/phpunit --filter testGetActiveSwitchFlagReturnsTrueSchema changes are tracked in the schema/ directory. To update your database:
- Write unit tests first (TDD approach)
- Implement data models and mappers
- Update schema files
- Create actions/controllers
- Add templates and JavaScript
- Run tests to verify
Check the flag's type and temporal constraints:
- SWITCH flags return
falsewhen inactive (notnull) - Other types return
nullwhen inactive - Verify start/end datetimes are correct
Verify SMTP configuration in etc/config.ini:
# Test SMTP connection
php -r "
require 'vendor/autoload.php';
\$smtp = new PHPMailer\PHPMailer\SMTP();
\$smtp->setDebugLevel(2);
\$smtp->connect('smtp.example.com', 587);
"Verify credentials in etc/config.ini and ensure database server is running:
# MySQL
mysql -u phlag_user -p -h localhost phlag
# PostgreSQL
psql -U phlag_user -h localhost -d phlagContributions are welcome! Phlag follows strict coding standards:
- PSR-1 and PSR-12 compliance
- 1TBS brace style
- snake_case for variables/properties
- camelCase for methods
- Type declarations on all methods
- Protected visibility (not private) unless truly encapsulated
- PHPDoc in Knowledge Base conversational style
See AGENTS.md for complete coding standards and architecture details.
BSD 3-Clause License
Copyright (c) 2025, Brian Moon
See LICENSE file for full text.
Built by Brian Moon ([email protected])
Key Dependencies:
- PageMill Router - Routing
- DealNews DataMapper - ORM
- Twig - Templating
- PHPMailer - Email
For bugs and feature requests, please use the GitHub issue tracker.
For questions and discussion, contact [email protected].