Download the PHP package flexagon/framework without Composer
On this page you can find all versions of the php package flexagon/framework. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download flexagon/framework
More information about flexagon/framework
Files in flexagon/framework
Package framework
Short Description Flexagon : a lightweight PHP web framework with no runtime dependencies
License MIT
Homepage https://flexagon.org
Informations about the package framework
Flexagon
Flexagon is a lightweight, high-performance PHP web framework.
It embraces PHP's inherent simplicity over complex abstractions, keeping conventions to a minimum so you can build applications quickly.
- Simple, flexible routing --- The file structure maps directly to the URL structure, keeping routing intuitive and straightforward.
- Independent request execution --- Each request leaves no unnecessary state behind, keeping behavior stable and predictable.
- Simple, idiomatic PHP development --- Build with PHP in its natural form, without introducing a separate template language.
Why Flexagon?
If you know PHP, you can get started right away.
Pages are .php files, views are written directly in PHP and HTML, and you can use the SQL you already know. There is no routing syntax, service container, or query DSL to learn before you start building.
Zero runtime dependencies.
Flexagon runs with PHP 8.3 and the pdo, openssl, and json extensions. There is no need to install dozens of third-party packages just to use the framework.
Small and fast.
In the same environment, framework bootstrap takes 1.09 ms, 2 MB of memory, and 15 loaded files. The framework itself remains compact at 39 PHP files and 7,148 lines of code.
| Flexagon 3.5.0 | Laravel 13.26.1 | |
|---|---|---|
| Runtime dependencies | 0 | 77 packages |
| Installed size | 532KB (PHAR 237KB) | 42MB |
| Bootstrap time | 1.09ms | 12.85ms |
| Bootstrap memory | 2MB | 20MB |
| Files loaded at bootstrap | 15 | 336 |
Simple deployment.
The entire framework can be deployed as a single 237 KB PHAR file. The target server does not need Composer or a separate cache-warming step.
Design Principles
URLs map directly to file paths
A request to /user/profile maps directly to public/user/profile.php.
There is no routing table to maintain. Create a file and it gets a URL; move the file and the URL moves with it. The URL itself makes it easy to see which file will be executed.
The database is the source of truth for the schema
On the first query, a DAO runs DESCRIBE to read column, primary-key, and auto_increment metadata, then reuses it for the rest of the request.
A schema already defined in the database does not need to be duplicated in PHP. When the schema changes, there is no separate mapping definition to keep in sync.
Global configuration stays explicit
Configuration is collected in the static _Global class.
There is no DI container or service locator between you and the configuration, so it is easy to see where settings live and which values are in use. Type _Global:: in your IDE to discover the available settings through autocompletion.
Minimal overhead per request
Framework bootstrap consists of defining constants, registering the autoloader, and loading configuration files.
Flexagon does not eagerly create unnecessary objects or services, and DataSourceManager reuses database connections within each request.
Requirements
| Item | Version / Notes |
|---|---|
| PHP | 8.3 or later |
| Extensions | pdo_mysql, openssl, json |
| Web server | Apache (mod_rewrite) or nginx |
| Database | MySQL / MariaDB, MS SQL Server |
Installation
Flexagon is distributed as two Composer packages.
| Package | Purpose |
|---|---|
flexagon/framework |
Framework core (library) |
flexagon/skeleton |
Application skeleton (project template) |
New Project
You can update the framework independently.
Add to an Existing Project
init creates the 18 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.
Installing only
flexagon/frameworkgives you immediate access to utility classes such asStringUtil,ValidUtil, andCryptoUtil, as well asBaseModel. However, the DAO layer requires the application to define_Global. Without it,BaseGlobaldefaults 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 three 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 |
| 3 | application/_Flexagon.phar/Bootstrap.php |
Source directory |
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.
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.
Docker
The included docker-compose.yml provides a PHP 8.3 + Apache + Xdebug environment.
Database Configuration
Edit application/_Config.php.
Your First Page
Open http://127.0.0.1:9001/hello to render the page.
Environment Check
Verify that the project layout and runtime configuration are consistent.
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.
Directory Structure
Under application/, namespaces map directly to directory paths. ExampleUser\UserDAO → application/ExampleUser/UserDAO.php.
The _Flexagon\ namespace resolves to the framework itself and normally does not need to be defined by the application.
Expose only public/ to the web. application/_Config.php contains database credentials; if the document root points to the project root, those files may be served directly.
Request Lifecycle
The three hooks run at distinct stages of the request lifecycle.
| File | Stage | Purpose |
|---|---|---|
_entry.php |
Before session | Site metadata, response headers, locale |
_prepare.php |
After session | Authentication/authorization checks, shared data loading |
_router.php |
Dispatch | Executes the page. Replace this file when custom routing is needed |
URL Mapping Rules
| Request URL | Executed file |
|---|---|
/ |
public/index.php |
/hello |
public/hello.php |
/user/profile |
public/user/profile.php |
/user/ |
public/user/index.php |
/user/profile?id=3 |
public/user/profile.php |
Request paths are normalized first by HttpUtil::normalizePath(). It removes NUL bytes, converts backslashes to /, and removes empty segments, ., and .., preventing traversal to parent directories.
TemplateLoader then resolves the final path with realpath() and includes it only after verifying that it remains inside public/ (or _Template/). Paths outside that boundary are rejected with a 404. This protection is enforced by the framework itself rather than relying solely on web-server path normalization.
The parsed result is stored in _Global::$URL_PARAM (UrlParamsModel).
Custom Routing
To dispatch requests manually instead of using file mapping, replace public/_router.php.
Configuration
_Global
All global configuration values are static properties of the _Global class, which extends BaseGlobal.
Key configuration options:
| Property | Default | Description |
|---|---|---|
$SITE_TITLE |
'Flexagon' |
Site title |
$TIMEZONE |
'Asia/Seoul' |
Passed to date_default_timezone_set() |
$DATA_SOURCES |
[] |
Data-source map (see below) |
$DATA_SOURCE_ID |
'default' |
Default data-source ID |
$USE_COMPOSER |
true |
vendor/autoload.php auto-load |
$USE_OUTPUT_BUFFER |
true |
Wrap the entire request with ob_start() |
$DB_STATEMENT_CACHE_SIZE |
64 |
Number of prepared statements cached per connection. 0 prepares every time |
$DB_CONNECT_FAILURE_FATAL |
true |
Abort the request on DB connection failure. If false, connect() returns false |
$ENABLE_EXTENSION_PHP |
false |
If true, URLs must explicitly include .php |
$USE_AUTO_CREATED_AT_TIMESTAMP |
true |
@db_auto_timestamp insert automatic timestamping |
$USE_AUTO_UPDATED_AT_TIMESTAMP |
true |
@db_auto_timestamp update automatic timestamping |
$RUNNING_MODE |
FLEXAGON_CONST::RUNNING_MODE_PRODUCTION |
Runtime mode. The framework does not interpret this value; applications may use it for branching |
$DEBUG_ON |
false |
Debug flag for application use |
$DEBUG_QUERY_BREAKPOINT |
false |
after each query Mysql::debugQueryAfter() call (for debugger breakpoints) |
$SITE_CONFIG['web']['header'] |
— | HTTP headers added to every response |
$SESSION_AUTO_START |
false |
Restore session automatically during bootstrap |
$SESSION_NAME |
'session_data' |
Session cookie name |
$SESSION_DOMAIN |
'' |
Cookie domain (.example.com) |
$SESSION_COOKIE_SECURE |
null |
Cookie Secure flag. null enables it automatically for HTTPS requests |
$SESSION_COOKIE_HTTPONLY |
true |
Cookie HttpOnly flag |
$SESSION_COOKIE_SAMESITE |
'Lax' |
'Lax' · 'Strict' · 'None'. 'None'automatically enables Secure |
$SESSION_MODEL_CLASSES |
[] |
Classes allowed in session cookies. If empty, only BaseModel inheritance is checked |
$SESSION_TIMEOUT_SECONDS |
86400 |
Session lifetime (seconds) |
$SESSION_REQUIRE_EXPIRY |
false |
Reject legacy cookies without exp. Set to true after the migration period |
$SESSION_NOT_BEFORE |
0 |
Invalidate all sessions issued before this timestamp (stateless forced logout) |
$SESSION_ENCRYPTION_STRING |
'' |
Session encryption key (10characters or more) |
$CLASS_PROPERTY_ENCRYPTION_PASSPHRASE |
'' |
@encrypt property encryption key (10characters or more) |
Data Sources
You can register multiple data sources.
Each data source receives a unique ID based on sha1(username@host/dbName), and DataSourceManager reuses connections by that ID. Multiple DAOs pointing to the same database therefore share a single connection within the request.
Stage-specific Configuration
Place stage-specific configuration in application/_Config/<stage>/_Config.php and select it from the CLI with the runtimeStage argument.
The default stage directories are alpha, beta, and production.
Models and DAOs
Models
Extending BaseModel provides array/JSON conversion, encryption/decryption, and automatic timestamps. Declare properties as private with getters and setters; mapping is based on method names.
Methods provided by BaseModel:
| Method | Description |
|---|---|
getArray(bool $containsObject = true, bool $forceAll = false) |
Convert getters to an associative array |
setByArray(array $data) |
Populate through setters; both snake_case and camelCase keys are accepted |
getJson(int $flags = JSON_UNESCAPED_UNICODE) |
JSON serialization (nested objects are flattened to arrays) |
encrypt() / decrypt() |
@encrypt Encrypt/decrypt annotated properties |
getCreatedAt() / getUpdatedAt() |
Unix timestamp |
getCreatedAtDatetimeString() |
Y-m-d H:i:s string |
Because BaseModel implements JsonSerializable, json_encode($model) works directly.
setByArray() uses reflection to reconcile common type mismatches. Empty strings become null for nullable properties, and scalar values are converted to matching cases for backed-enum properties. This makes it practical to pass form input directly into a model.
DAOs
Extend BaseMySqlDAO and specify $tableName to get the core CRUD operations.
The CRUD methods are protected by design. Each DAO explicitly chooses which operations to expose, keeping its public interface narrow and aligned with the domain.
| Method | Description |
|---|---|
_insert($modelOrArray) |
INSERT. Returns the auto-increment value (false on failure) |
_update($modelOrArray) |
UPDATE by primary key |
_updateByQuery($modelOrArray, $whereSql, $params) |
UPDATE with a custom condition |
_select($modelOrArray, ?object $vo) |
Fetch one row; returns an object when $vo is provided, otherwise an array |
_selectByQuery($whereSql, $params, ?object $vo) |
Fetch one row with a custom condition |
_selectList(?object $vo, $page, $perPage, $whereSql, $params, $orderSql) |
Fetch a paginated list |
_selectTotalCount($whereSql, $params) |
Count matching rows |
_delete($modelOrArray) |
DELETE by primary key |
_deleteByQuery($whereSql, $params) |
DELETE with a custom condition |
_insertAll($modelList, $chunkSize = 500) |
Bulk INSERT using one statement per chunk; returns the number of inserted rows |
_truncate() |
TRUNCATE |
When using SQL Server, BaseMsSqlDAO provides the same method surface. Both DAO implementations extend BaseSqlDAO, where CRUD operations, schema caching, and transaction handling are shared. Subclasses contain only dialect-specific behavior: identifier quoting (`name` / [name]), schema inspection (DESCRIBE / INFORMATION_SCHEMA), and pagination (LIMIT / OFFSET·FETCH).
When you need direct SQL, use $this->db (a Mysql instance).
Column ↔ Property Mapping
Database snake_case columns are mapped automatically to PHP camelCase properties.
| DB column | Getter / Setter | Property |
|---|---|---|
id |
getId() / setId() |
$id |
user_name |
getUserName() / setUserName() |
$userName |
created_at |
getCreatedAt() / setCreatedAt() |
$createdAt |
Parameter Binding
Queries use PDO prepared statements with named placeholders. Framework-generated queries use the uppercase column name as the placeholder (user_name → :USER_NAME).
If the same placeholder appears multiple times in a query, SqlUtil::expandQueryParams() automatically expands it into distinct parameters, avoiding PDO's duplicate-binding limitation.
Debugging
When _Global::$DEBUG_QUERY_BREAKPOINT is enabled, Mysql::debugQueryAfter() is called after every query. The method is intentionally empty, making it a convenient IDE breakpoint for inspecting executed queries.
Annotations
Flexagon reads PHPDoc tags at runtime to modify behavior (PhpDocUtil).
The canonical notation is snake_case.
Tag names are case-insensitive and separator-insensitive. The following forms are equivalent.
Tag values are case-insensitive, but separators remain significant (insert = INSERT, while my_value ≠ myvalue).
Both single-line docblocks (/** @db_auto_timestamp insert */) and multiline docblocks are supported.
@db_auto_timestamp
Automatically fills Unix timestamps on INSERT and UPDATE. This is already applied to createdAt and updatedAt in BaseModel.
@encrypt
Automatically encrypts and decrypts properties with AES-256-CBC. Values are encrypted before being stored in the database and decrypted when query results are mapped back to objects.
Ciphertext uses the format base64(version || IV || HMAC-SHA256 || ciphertext). A fresh IV is generated for every operation, so identical plaintext produces different ciphertext each time. Before decryption, the HMAC is verified with hash_equals() to reject tampered data. Encryption and authentication keys are derived separately from the passphrase using hash_hkdf().
_Global::$CLASS_PROPERTY_ENCRYPTION_PASSPHRASE must be at least 10 characters long or an exception is thrown. Make sure the database column is large enough to hold the ciphertext.
@exclude_from_get_array
Excludes a getter from getArray() / getJson() output. Use it for values such as password hashes that should not be serialized.
Calling getArray(forceAll: true) ignores exclusions and returns all values. DAOs use this mode internally when writing to the database.
See ANNOTATIONS.md for the complete list, constraints, and instructions for defining custom annotations.
Transactions and Multiple Data Sources
Single DAO
Transactions Across Multiple DAOs
Start a global transaction with DataSourceManager, and every data source used afterward automatically participates in the transaction.
Commits and rollbacks are performed in the reverse order in which connections were registered.
Assigning a Data Source to a DAO
Override the DATA_SOURCE_ID constant.
Sessions
Flexagon sessions serialize model objects into encrypted cookies rather than server-side files. Because there is no shared session store, horizontally scaling across multiple web servers requires no additional session-storage configuration.
Configuration
Session cookies carry authentication state, so HttpOnly and SameSite=Lax are enabled by default, and Secure is added automatically for HTTPS requests. HTTPS detection checks $_SERVER['HTTPS'], port 443, and then X-Forwarded-Proto.
Session Model and DAO
Usage
When $SESSION_AUTO_START is enabled, the session is restored during bootstrap and exposed in two locations.
The second form is derived by converting the model class name to UPPER_SNAKE_CASE (SessionUserModel → SESSION_USER_MODEL). To access it with a type hint, declare the corresponding static property in _Global in advance.
A common pattern is to perform authorization checks in _prepare.php.
Note: A session-model property marked with
@encryptis encrypted twice inside the cookie. Because the entire session cookie is already encrypted with$SESSION_ENCRYPTION_STRING, the additional annotation is usually unnecessary.
Security
What the Framework Provides
| Item | Description |
|---|---|
| SQL injection | All DAO queries use PDO prepared statements |
| Path traversal | Router include paths cannot escape public/ |
| Session integrity | AES-256-CBC + HMAC-SHA256, expiration verified server-side |
| Session cookies | HttpOnly·SameSite enabled by default; Secure automatic |
| Column encryption | @encrypt — random IV, HMAC verification, HKDF key derivation |
| Forced logout | $SESSION_NOT_BEFOREinvalidates all sessions (stateless) |
Application Responsibilities
These features are intentionally not provided by the framework. Do not assume Flexagon handles them automatically.
The application is also responsible for validating redirect targets, preventing SSRF, sanitizing HTML, and setting appropriate security headers.
The
HtmlUtil::stripSpecificTag()family is regex-based and is not an XSS filter. These methods are convenience helpers for cleaning up tags only.
Templates and Assets
TemplateLoader
The supplied array is passed through extract() and exposed as local variables inside the template.
AssetLoader
Collect CSS and JavaScript assets and render them together at the desired location. A version query string can be added for cache invalidation.
- Relative paths are prefixed with
assetRootPath + dirPath; URLs beginning withhttp://,https://, or//are left unchanged. - Preload hints are rendered separately with
printPreloads(). Place them in<head>while leaving script tags at theprintScripts()location (typically just before</body>) so scripts can be fetched while the document body is being parsed. - Preload hints are not added to absolute URLs because a mismatched
crossoriginsetting may cause the browser to download the resource twice. - Each
print*()method renders one asset type, and the template controls placement:printPreloads()andprintStyles()belong in<head>, whileprintScripts()typically goes just before</body>. - Use
unsetJs('analytics.js')to remove a previously registered script.
Utility Libraries
All utilities use static methods and have no external dependencies. Their namespace is _Flexagon\Libs.
| Class | Key Features |
|---|---|
StringUtil |
case conversion, UTF-8 cleanup, hex encoding, random string generation |
ArrayUtil |
camelCase key conversion, array diff, nested-array key lookup |
ValidUtil |
email, integer, float, alphanumeric, and ID validation |
CryptoUtil |
AES-256-CBC encryption/decryption (strings / arrays, random IV support) |
FileUtil |
directory traversal, recursive creation, extension detection |
HttpUtil |
URL parsing, query-string parsing, request-path resolution |
NetUtil |
GET/POST HTTP client, JSON response parsing, file uploads |
HtmlUtil |
redirects, HTML tag/attribute removal, image URL extraction |
TimeDateUtil |
timestamp conversion, timezone offsets, relative time formatting |
SqlUtil |
parameter normalization/expansion, SQL escaping, SELECT → COUNT conversion |
ClassUtil |
class name, path, property, and caller inspection |
PhpDocUtil |
PHPDoc tag parsing (basis of annotation support) |
AssetLoader |
CSS/JS management |
TemplateLoader |
template loading |
DataSourceManager |
connection reuse and global transactions |
Global Helper Functions
The three functions defined by Libs/_Util.php are available globally without a namespace.
When gettext (__() or _()) is available, translation is applied before placeholder substitution.
Enum Helpers
Add the EnumSupporter trait to a backed enum to enable convenience methods.
When generating DDL for an ENUM column, use toQuotedCommaString(). It quotes each value and escapes embedded quotes by doubling them.
For pure enums without backing values, keys() and toCommaString() return case names, while values() and toArray() return null values.
Enum-typed properties are also handled automatically by DAO mapping. Scalar database values are converted to matching enum cases when models are hydrated, and ->value is bound when values are stored.
CLI Scripts
Place batch jobs in the script/ directory.
Arguments in key=value form are parsed into both $_PARAMS and $_REQUEST. runtimeStage is reserved and selects the configuration directory to load.
When running from the CLI, FLEXAGON_ENV::$_RUNTIME_ENV is set to RUNTIME_ENV_SCRIPT, bypassing the web request pipeline entirely, including sessions, routing, and output buffering.
Framework CLI
The framework itself provides two commands.
| Invocation | Supported installation types |
|---|---|
php script/flexagon.php |
All (Composer · PHAR · source) |
php vendor/bin/flexagon |
Composer installation |
A PHAR cannot be passed to the PHP CLI as a
phar://...script path, soscript/flexagon.phpacts as the entry point. The PHAR does not contain templates forinit, so onlycheckis available in that installation mode.
init
Creates the 18 files that make up the project structure. If you started with composer create-project flexagon/skeleton, these files already exist. This command is primarily useful when adding flexagon/framework to an existing project with composer require.
Existing files are left untouched. You can also run the command after an upgrade to add only newly introduced structure files.
Use --force if you need to overwrite existing files. Be careful: modified files will be replaced with the template versions.
check
Checks that the project layout and runtime configuration are consistent and returns exit code 1 when a problem is found, making it suitable for deployment pipelines.
| Item | Check | Result |
|---|---|---|
php |
8.3 or later | Error |
ext-json · ext-openssl · ext-pdo |
Loaded | Error |
opcache.save_comments |
Enabled | Error |
project layout |
flexagon.php · application/_Global.php · public/__flexagon.php · public/_router.php |
Error |
config placement |
_Config.php and application/ are not exposed under public/ |
Error |
bootstrap |
Framework boots successfully | Error |
session key |
$SESSION_ENCRYPTION_STRING length when $SESSION_AUTO_START is enabled |
Error |
data sources |
Configured; no template placeholders remain | Warning |
display_errors |
Enabled in production mode | Warning |
The following checks are especially important because these issues can otherwise fail silently.
- If
opcache.save_commentsis disabled,@encrypt,@db_auto_timestamp, and@exclude_from_get_arrayare all ignored. A column that should be encrypted may be stored in plaintext without any exception. - If
_Config.phpis placed underpublic/, database credentials may be served directly if PHP handling is unavailable or misconfigured. - If a data source still contains the default
username/dbnameplaceholders, the connection simply fails.
License
MIT License
Copyright (c) 2010-2026 Younghwan Yong
All versions of framework with dependencies
ext-json Version *
ext-openssl Version *
ext-pdo Version *