Revisions and Migrations
FrankPHP is intentionally simple to ship: a versioned Zip, a small set of framework files, and clear context files that explain how the framework behaves. That simplicity does not mean every upgrade should be treated as a file overwrite. A FrankPHP revision can contain two different kinds of change:- changes that affect new installs only
- changes that must be applied carefully to existing applications with live data
The core rule
Treat every FrankPHP release as two related but separate deliverables:- the framework revision: files, docs, conventions, route changes, service changes, schema templates
- the migration path: the deliberate steps needed to bring an existing application forward safely
What counts as a revision?
A revision is the complete framework state for a released version. For FrankPHP, that normally includes:VERSIONCHANGELOG.mdCODEBASE.mdRELEASE_MANIFEST.json- framework-owned PHP files
sql/schema.sql- any migration scripts for existing installs
- updated documentation
What is true about FrankPHP at this version?The migration answers a different question:
How does an existing application safely become compatible with this version?Both are needed.
Fresh install versus existing install
A fresh install has no customer data yet. It can use the latest framework schema directly. An existing install already has users, tenants, settings, timestamps, and application-owned tables. That data must be preserved. Use this decision table:The v1.3.0 example
FrankPHP v1.3.0 introduced the framework’s first formal date/time standard:UTC at rest. Local at the edges.It also introduced
App\Core\Clock, the framework-owned utility for UTC timestamp generation, timezone resolution, UTC/local conversion, and local-date query boundary calculation.
That change affects both new installs and existing applications.
For a new install, the corrected v1.3.0 schema can declare framework timestamp columns as UTC DATETIME from the start.
For an existing install, the migration needs to inspect the current schema, guard against duplicate identity data, convert compatible TIMESTAMP columns safely, and avoid making assumptions about historical DATETIME values.
Recommended upgrade workflow
Follow this workflow for each FrankPHP release.Step 1: Read the changelog entry
Start withCHANGELOG.md.
Look specifically for:
- added framework utilities
- changed framework rules
- database changes
- migration notes
- build rules introduced for AI-assisted development
App\Core\Clock, while local timezones are used only at input, output, and query-boundary edges.
Step 2: Classify the release
Ask what kind of release this is.
v1.3.0 is a framework hygiene release, but it is also schema- and data-relevant because it formalises timestamp storage expectations and migration handling for MySQL timestamp columns.
Step 3: Update framework files deliberately
Framework-owned files should be updated as a versioned framework upgrade, not mixed into ordinary application feature work. Typical framework-owned files include:Core/Clock.php- framework models such as
Models/User.phpandModels/Tenant.php - framework auth/signup services
bootstrap.phpsql/schema.sqlCODEBASE.mdCHANGELOG.md
FrankPHP deliberately separates framework-owned files from application-owned files. Application features should continue to be documented in
MYAPP.md; framework conventions belong in CODEBASE.md.Step 4: Run migration preflight checks
Before changing a live database, run the release’s preflight checks. For v1.3.0, one important preflight check is platform-wide email uniqueness:UNIQUE(email) index.
This is a product/account ownership decision, not a technical detail the migration should guess.
Step 5: Run the version-specific migration script
Use the migration script for the release, not the fresh-install schema. For v1.3.0, existing applications should use:- detecting duplicate emails before enforcing global identity uniqueness
- adding missing framework columns only when required
- standardising the database session timezone during timestamp conversion
- converting compatible framework
TIMESTAMPcolumns toDATETIME - producing post-migration review queries
Step 6: Treat historical timestamps carefully
MySQLDATETIME stores a date and time but no timezone metadata.
That means a migration cannot automatically know whether an old value was:
- already UTC
- local wall-clock time
- generated by PHP on a server using local timezone
- generated by SQL using the database session timezone
Step 7: Update application code to follow the new contract
For v1.3.0, search application code for direct date/time usage:App\Core\Clock patterns.
The goal is not to make every line of date code look identical. The goal is to make the responsibility boundary consistent:
Step 8: Update MYAPP.md
If the application has date/time behaviour, record it in MYAPP.md.
Useful things to document include:
- which application tables store UTC
DATETIMEvalues - which columns are intentionally
DATE - any historical timestamp corrections made during migration
- any deliberate deviation from the framework rule
- any application-specific timezone source beyond user, tenant, config, and UTC fallback
A deliberate deviation can be documented. A view performing timezone conversion should not be treated as a deviation. It should be refactored.
What to keep with each release
A good FrankPHP release folder should leave you with enough information to answer three questions later:- What changed?
- Why did it change?
- How did existing installs move safely?
- the release Zip
CHANGELOG.mdCODEBASE.md- the fresh-install schema
migration_v_1.3.0.sql- notes from any project-specific timestamp correction
- the updated application
MYAPP.md
Practical release checklist
Use this as the short version before upgrading an existing app.- Read the release changelog
- Identify framework-owned file changes
- Identify schema and data changes
- Back up the database
- Run preflight checks
- Resolve duplicate identity records before migration
- Run the version-specific migration script
- Review post-migration output
- Search application code for old date/time patterns
- Replace persisted timestamp writes with
Clock - Replace SQL local-date filters with UTC boundaries
- Keep views free from timezone conversion
- Update
MYAPP.md