Installation

Requirements

Item Version / Notes
PHP 8.3 or later
Extensions pdo_mysql (or pdo_pgsql, pdo_sqlite, pdo_sqlsrv/pdo_dblib), openssl, json
Web server Apache (mod_rewrite) or nginx
Database MySQL / MariaDB, PostgreSQL, SQLite, MS SQL Server

Flexagon is distributed as two Composer packages.

Package Purpose
flexagon/framework Framework core (library)
flexagon/skeleton Application skeleton (project template)

New Project

composer create-project flexagon/skeleton myapp
cd myapp
myapp/
├── application/          # Domain code
├── public/               # ← document root
├── script/               # CLI jobs
├── flexagon.php
├── composer.json
└── vendor/
    └── flexagon/framework/   # Framework core

You can update the framework independently.

composer update flexagon/framework

Add to an Existing Project

composer require flexagon/framework
composer exec flexagon init      # or php vendor/bin/flexagon init

init creates the 34 files that make up the project structure, including public/, application/, script/, and flexagon.php. Existing files are left untouched, so you can also use it after an upgrade to add only newly introduced structure files. Use --force to overwrite existing files.

create   flexagon.php
create   application/_Global.php
...
keep     public/index.php        ← keep existing file

Installing only flexagon/framework gives you immediate access to utility classes such as StringUtil, ValidUtil, and CryptoUtil, as well as BaseModel. However, the DAO layer requires the application to define _Global. Without it, BaseGlobal defaults are used, so no fatal error occurs, but the empty data-source configuration prevents database connections. For real applications, start with the skeleton package.

flexagon.php checks two layouts in order, so the entry-point code remains the same regardless of how the framework is installed.

Order Path Scenario
1 vendor/flexagon/framework/Bootstrap.php Composer installation
2 application/_Flexagon.phar (file) Packaged as a single PHAR

If neither is found, it stops with a RuntimeException that tells you to run composer install or build the PHAR with ant build. vendor/autoload.php is loaded before the framework so that Composer files entries are registered first.

Bootstrap.php finds the nearest parent directory containing application/_Global.php and uses it as PROJECT_ROOT. Application path resolution therefore works the same whether the framework lives under vendor/ or inside a PHAR.

Autoloading

Application classes are mapped to application/ through PSR-4 in composer.json.

ExampleUser\UserDAO  →  application/ExampleUser/UserDAO.php

To add a namespace, simply create a directory with the same name. There is no need to modify composer.json.

The framework automatically loads the _Flexagon\ namespace, global helpers (_echo, _t, ___), and TemplateLoader. No additional configuration is required.

Database Configuration

Edit application/_Config.php.

<?php
use _Flexagon\Libs\Env;
use _Flexagon\Models\DataSourceModel;

_Global::$DATA_SOURCES['default'] = new DataSourceModel(
    Env::get('DB_HOST', 'localhost'),
    Env::int('DB_PORT', 3306),
    Env::get('DB_USER'),
    Env::required('DB_PASSWORD'),
    Env::get('DB_NAME')
);

The values come from .env.php or the environment; see the next section.

Secrets (.env.php)

Keep passwords and keys out of application/. Copy .env.example.php to .env.php in the project root and fill it in; _Config.php reads the values with Env.

<?php
// .env.php — ignored by git, never under public/
return [
    'DB_USER'     => 'app',
    'DB_PASSWORD' => 's3cr3t',
    'DB_NAME'     => 'service',
    'SESSION_ENCRYPTION_STRING' => '...',
];
Method Returns
Env::get($key, $default = null) The value, or $default
Env::required($key) The value; throws when it is missing or empty
Env::int($key, $default) / Env::bool($key, $default) Typed value; throws when it does not parse
Env::has($key) Whether the key is set

A value is looked up in the process environment first, then .env.php, then the default. In production you can set the same keys through PHP-FPM env[...], Docker or your platform, and leave .env.php out. The file is plain PHP, so opcache keeps it compiled and values keep their types. It is read the first time Env is used, so an application that does not use it pays nothing. Define ENV_FILE before boot, or call Env::load($path), to read another file. How to keep each server's .env.php outside the project (in /etc/flexagon/ or the like) is in application/_Config/README.md.

Nothing is written to putenv() or $_ENV, error messages name the key but never the value, and var_dump() of a DataSourceModel shows the password as ***.

flexagon check fails when .env.php is under public/ or tracked by git, and warns about keys from .env.example.php that are not set, a password written directly into _Config.php, a leftover .env (Flexagon does not read it), and opcache.file_cache (which writes the compiled secrets to disk).

Keeping AI coding agents out

With the secrets in .env.php, the files under application/ are safe for an agent to read. Block the one file in the agent itself. For Claude Code the skeleton ships .claude/settings.json (and flexagon init creates it when missing):

{
  "permissions": {
    "deny": ["Read(./.env.php)", "Read(./**/.env.php)"]
  },
  "sandbox": {
    "filesystem": { "denyRead": ["./.env.php"] }
  }
}

permissions.deny stops the Read tool; sandbox.filesystem.denyRead also stops shell commands such as cat .env.php. Use both. Other agents have their own ignore settings. An agent that can run code can still print a value at runtime (php -r 'echo Env::get(...)'), so give it a development .env.php with development credentials, not production ones.

Your First Page

<?php
// public/hello.php
_Global::$SITE_TITLE = 'Hello';

TemplateLoader::show('head');
echo '<h1>Hello, Flexagon</h1>';
TemplateLoader::show('tail');

Open http://127.0.0.1:9001/hello to render the page.

Environment Check

Verify that the project layout and runtime configuration are consistent.

php script/flexagon.php check     # works with any installation type
php vendor/bin/flexagon check     # also available with Composer installations
ok       php 8.3.33
ok       opcache.save_comments
ok       project layout
ok       bootstrap
warn     data sources  default still contains template placeholders
ok       display_errors  off

It catches configuration mistakes that might otherwise fail silently. If opcache.save_comments is disabled, @encrypt and @db_auto_timestamp are ignored without an error. If _Config.php is placed under public/, database credentials may be exposed over the web. The command exits with status 1 when it finds a problem, making it suitable for deployment pipelines.

See Framework CLI for the complete list of checks.