This file contains essential information for AI coding agents working on the LibreBooking repository. Following these guidelines will help you work efficiently and maintain code quality.
LibreBooking is an open-source resource scheduling and booking system written in PHP. It's a fork of Booked Scheduler that has evolved significantly since 2020.
- Primary Language: PHP (>=8.2)
- Database: MySQL >= 8.0 (2018) or MariaDB >= 10.6 (2021)
- Architecture: Model-View-Presenter (MVP) pattern
- Template Engine: Smarty (version 5.8+)
- Frontend: Bootstrap 5, jQuery
- Main Branch:
develop(notmainormaster) - License: GPL-3.0
/config Application configuration files
/Controls Reusable page control objects
/database_schema SQL scripts for database setup and upgrades
/Domain Domain entities, repositories, services
/Access Database abstraction layer
/Events Domain events
/Values Value objects
/Jobs Scheduled tasks (cron jobs)
/lang Translation files (30+ languages)
/lib Application supporting objects
/Application Application logic (admin, auth, reservations, etc.)
/Common Shared utilities (dates, localization, etc.)
/Database Database access and abstractions
/Email Email services
/external Third-party libraries (DO NOT MODIFY)
/Pages Page binding and workflow logic
/Presenters Application logic and page population
/plugins Plugin architecture (various types)
/src PSR-4 autoloaded code, namespace LibreBooking\ (migration in progress)
/tests PHPUnit test suite
/tpl Smarty templates
/tpl_c Template cache (auto-generated, not tracked)
/Web User-facing pages and assets
/scripts JavaScript files
/css Stylesheets
/WebServices REST API implementation
- PHP >= 8.2 with extensions: ctype, curl, fileinfo, intl, json, ldap, mbstring, mysqli, openssl, pdo, pdo_mysql, tokenizer, xml
- Optional PHP extensions: bcmath, gd
- Composer for dependency management
- MySQL >= 8.0 (2018) or MariaDB >= 10.6 (2021) for database
- Git for version control
-
Clone the repository:
git clone https://github.com/LibreBooking/librebooking.git cd librebooking -
Install dependencies:
composer install
⚠️ Known Issue - Composer Authentication Error: If you encounterCould not authenticate against github.comduringcomposer install, this is a known issue in certain CI/sandboxed environments.Workarounds:
- The error typically occurs when Composer tries to download packages from GitHub without proper authentication
- If in a CI environment, ensure GitHub tokens are properly configured
- In some cases, packages may already be cached and installation can proceed despite the error
- If the error persists and blocks progress, note this limitation and proceed with tasks that don't require vendor dependencies
-
Configure the application:
cp config/config.dist.php config/config.php # Edit config.php with your database settings -
Set up the database (if needed):
- Use Phing tasks:
composer build(requires MySQL credentials) - Or manually run SQL scripts in
/database_schema/
- Use Phing tasks:
Run the preflight checker to validate your environment:
composer preflight
# Or: php lib/preflight.phpThis script checks PHP version, required extensions, permissions, and optional database connectivity.
PHP Syntax Check:
./ci/ci-phplintThis uses parallel processing to lint all PHP files (excluding vendor, tmp, node_modules).
PHP-CS-Fixer (PSR-12 compliance):
# Check code style (dry-run, no changes)
composer phpcsfixer:lint
# Fix code style issues automatically
composer phpcsfixer:fixConfiguration: .php-cs-fixer.dist.php
- Follows PSR-12 standards
- Uses short array syntax
- Enforces single quotes
- Cache:
var/cache/.php-cs-fixer.cache
PHPStan (level 2):
# Run base analysis
composer phpstan
# Run with stricter rules
composer phpstan_nextConfiguration files:
phpstan.neon- Base configuration (level 2)phpstan_next.neon- Stricter analysis for progressive improvements
Output format: prettyJson
Cache directory: var/cache/phpstan/
PHPUnit (version 11.5+):
# Run all tests (excludes integration tests)
composer phpunit
# Or: ./vendor/bin/phpunit
# Run specific test suite
composer phpunit -- --testsuite domain
composer phpunit -- --testsuite application
# Run integration tests (requires database)
composer test:integrationConfiguration: phpunit.xml.dist
Test suites available:
all- All tests except integration (default)application- Application layer testsdomain- Domain layer testsplugins- Plugin testspresenters- Presenter testssrc- Tests for PSR-4 code undersrc/webservice/webservices- API testsintegration- Integration tests (requires database setup)
Important: Integration tests require a configured database. Set up config.php before running.
Run both tests and linting:
composer testFrontend JavaScript and CSS linting uses npm tooling. Node.js >=20.19.0 is required.
# Install frontend dev dependencies
npm ci
# Check frontend JavaScript, CSS, and formatting
npm run lint:frontend
# Auto-fix frontend formatting/lint issues
npm run fix:frontendIndividual commands are also available: npm run lint:js, npm run lint:css,
npm run lint:format, npm run fix:js, npm run fix:css, and
npm run fix:format.
Generate API documentation with PHPDocumentor:
# Install phive tools first
composer install-tools
# Generate docs (output: .phpdoc/build/)
./tools/phpdocConfiguration: phpdoc.dist.xml
Build a distributable release package:
composer build
# Uses Phing, output: build/librebooking.zipConfiguration: build.xml
- Follow PSR-12 - The codebase strictly follows PSR-12 standards
- Use PHP 8.2+ features where appropriate (enums, readonly properties, constructor property promotion, etc.)
- Type hints - Use strict typing where possible
- Documentation - Add PHPDoc blocks for classes and complex methods
- No trailing whitespace - EditorConfig enforces this
- LF line endings - Unix-style line endings (see
.editorconfig) - 4-space indentation for PHP, 2-space for YAML files
- Single quotes for strings (unless interpolation is needed)
- Short array syntax - Use
[]notarray() - No magic numbers - Use named constants, enums, or class constants instead of raw numeric literals. For example, use
CustomAttributeTypes::CHECKBOXinstead of4. Code reviews should flag any unexplained numeric literals as magic numbers
Model-View-Presenter (MVP):
- Pages (
/Pages/*.php) - Thin abstraction to template engine, minimal logic - Presenters (
/Presenters/*.php) - Orchestrate interactions, fetch/transform data - Domain (
/Domain/) - Business logic, entities, repositories
File Organization:
- Each page should have a corresponding template in
/tpl - Each page should have a class in
/Pages - Each page class should have a presenter in
/Presenters - Related code grouped in directories with
namespace.phpfor easy inclusion (legacy pattern; new self-contained code uses PSR-4 in/src— see "PSR-4 Migration" below)
The project is gradually migrating from require_once include chains to
Composer PSR-4 autoloading. composer.json maps the LibreBooking\ namespace
to src/, and CI runs composer dump-autoload --optimize --strict-psr, so
everything under src/ must be strictly PSR-4 compliant.
Where new code goes:
- New self-contained classes (value objects, helpers, services that do not
extend or implement anything from the legacy include system) go in
src/under theLibreBooking\namespace. - Code extending an existing legacy subsystem (a new Presenter, Page, or a
class implementing a legacy global interface) stays in the existing
directory layout for now. The legacy
namespace.phpinclude chains reference global class names and cannot see namespaced classes.
Rules for code in src/:
- Exactly one class, enum, interface, or trait per file; the filename must match the type name.
- The namespace mirrors the path:
src/Application/Authentication/Foo.phpdeclaresLibreBooking\Application\Authentication\Foo. - Start every file with
declare(strict_types=1);. - Never add the legacy trees (
lib/,Domain/,Pages/, etc.) to the PSR-4 autoload mapping incomposer.json— they are not PSR-4 compliant and the strict CI check will fail. - Run
composer dump-autoloadafter adding or moving files (never commit anything undervendor/). - Tests live in
tests/src/, mirroring the source path, and are namespacedLibreBooking\Tests\...(mapped viaautoload-dev). The strict CI autoload check enforces that test namespaces and class names match their paths. Run them alone withcomposer phpunit -- --testsuite src.
Migrating a legacy file to src/:
- Split multi-type files so each class/enum gets its own file.
- Add the namespace and update every call site with
useimports. - Delete the old file from the legacy tree.
- Move the class's tests to
tests/src/and namespace themLibreBooking\Tests\...(see Testing Strategy). - Web entry points (
Web/*.php) must keeprequire_once ROOT_DIR . 'lib/Common/namespace.php';in addition torequire_once ROOT_DIR . 'vendor/autoload.php';. The Composer autoloader cannot load legacy global classes such asConfigurationandLog, and the legacy bootstrap also registers the global exception handler. Dropping it causes fatals that only appear at runtime (see the OAuth regression in PR #1552). - When changing an entry point's bootstrap, add an entry-point-level
regression test that executes the script in a subprocess (see
tests/Web/MicrosoftAuthEntryPointTest.phpfor the pattern).
src/ is already covered by php-cs-fixer, PHPStan (phpstan.neon and
phpstan_next.neon list it in paths), release packaging, and phplint, so no
per-PR tooling changes are needed when adding files there.
- Classes: PascalCase (e.g.,
ReservationService,UserRepository) - Methods: camelCase (e.g.,
getUserById,validateReservation) - Variables: camelCase
- Constants: UPPER_SNAKE_CASE
- Files: Match class names (e.g.,
ReservationService.php)
- Uses MySQL/MariaDB
- Entity Relationship Diagram:
docs/source/ERD.svg - Schema management via Phing tasks or manual SQL scripts
- Migration scripts in
/database_schema/upgrades/
Important: The main development branch is develop, NOT main or master.
- develop - Active development, latest beta code
- master - Stable releases only
- Feature branches:
feature/description-of-feature - Bugfix branches:
bugfix/issue-number-description
Follow conventional commits format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat- New featurefix- Bug fixdocs- Documentation changesstyle- Code style changes (formatting, no logic change)refactor- Code refactoringperf- Performance improvementstest- Test additions/correctionsbuild- Build system/CI changesci- CI configuration changeschore- Other changes
Scopes (optional but recommended):
API- API changes- Or describe the affected module/area
Rules:
- Header max 72 characters
- Use imperative, present tense ("change" not "changed")
- Reference GitHub issues in footer (e.g.,
Closes: #123) - Breaking changes: Start footer with
BREAKING CHANGE:
Example:
feat(API): Add new schedules endpoint
Add a new schedules endpoint which allows getting the resources of a schedule.
Closes: #2222
When an AI coding assistant contributed meaningfully to a commit (writing or
significantly modifying code, suggesting the fix, generating tests, etc.),
add an Assisted-by trailer to the commit message footer:
Assisted-by: <AgentName>:<ModelVersion>
Rules:
- Place
Assisted-byin the commit footer, alongside anyCloses:orBREAKING CHANGE:lines - Use the agent name and model version that did the work (e.g.
Claude:claude-sonnet-4-6) - Omit the tag when the AI contribution was trivial (e.g. a one-word suggestion accepted unchanged, or only autocomplete)
- Do not add
Assisted-byfor purely mechanical operations (running linters, reformatting, applyingcomposer fix)
Example:
fix(reservations): correct overlap check for multi-day bookings
The start-of-day boundary was calculated in UTC rather than the
schedule's local timezone, causing false conflicts.
Closes: #1234
Assisted-by: Claude:claude-sonnet-4-6
- Target branch: PRs should target
develop(not main/master) - No merge commits: PRs must not contain merge commits (use rebase)
- Linear history: Maintainers will use "Rebase and merge" or "Squash and merge"
- PR title: Follow commit message header format
- Update docs: If changing interfaces, update README.md and relevant docs
- Tests required: Add tests for new features/bug fixes when applicable
- CI must pass: All lint, analysis, and test checks must pass
The repository uses several CI workflows:
-
Linters (
lint-and-analyse-php.yml) - Runs on PRs todevelop:- Commitizen (commit message validation)
- Config checks
- doc8 (RST documentation)
- Markdown linting
- PHP lint (syntax check) on PHP 8.2, 8.3, 8.4, 8.5
- php-cs-fixer (code style) on PHP 8.2
- PHPStan analysis on PHP 8.5 (both base and next configs)
- Validates no merge commits in PR
-
PHPUnit (
phpunit.yml) - Runs on PRs todevelop:- Unit tests on PHP 8.2, 8.3, 8.4, 8.5
- Creates config.php from config.dist.php
- Validates test summary output
-
Integration Tests (
integration-tests.yml) - Database integration tests -
Documentation (
docs.yml) - Builds Sphinx documentation -
Release (
release.yml,release-dry-run.yml) - Automated releases with semantic-release
Before pushing, validate your changes locally:
# 1. Lint PHP syntax
./ci/ci-phplint
# 2. Check code style
composer phpcsfixer:lint
# 3. Run static analysis
composer phpstan
# 4. Run tests
composer phpunit
# 5. Preflight check
composer preflight-
Create feature branch from
develop:git checkout develop git pull git checkout -b feature/your-feature-name
-
Implement following MVP pattern:
- Create/update page in
/Web/ - Create page class in
/Pages/ - Create presenter in
/Presenters/ - Create template in
/tpl/ - Add domain logic in
/Domain/or/lib/Application/; prefer/src/for new self-contained classes (see "PSR-4 Migration" above)
- Create/update page in
-
Add tests in
/tests/matching the directory structure -
Update documentation if needed
-
Validate locally:
composer phpcsfixer:fix # Fix style issues composer phpstan # Check static analysis composer phpunit # Run tests
-
Commit with conventional commit message
-
Push and create PR to
develop
Plugins are located in /plugins/ with subdirectories by type:
Authentication/- Authentication providersAuthorization/- Authorization handlersPreReservation/- Pre-reservation logicPostReservation/- Post-reservation logic
Each plugin typically has a *.config.dist.php file for configuration.
Templates use Smarty syntax:
- Variables:
{$variableName} - Conditionals:
{if $condition}...{/if} - Loops:
{foreach $items as $item}...{/foreach} - Functions:
{translate key="some.key"}
Templates are cached in /tpl_c/ - this directory is auto-generated.
Use {vendor_js ...} and {vendor_css ...} for bundled frontend assets under
Web/assets/vendor/. Prefer local bundled assets over adding CDN references
unless a feature explicitly requires remote loading.
Use DatePickerSetupControl for date/date-time inputs. Client-side date and
time helpers live in Web/scripts/date-helper.js; prefer those helpers over
adding new Moment.js usage or ad hoc date parsing.
- For stored rich-text HTML such as announcements and resource
descriptions/notes, use the Smarty modifier
sanitize_rich_textbefore rendering live HTML. The modifier is backed byRichTextHtmlSanitizerinlib/Common/Security/. - Do not use
html_entity_decodedirectly for rich-text display paths. The rich-text sanitizer already decodes legacy entity-encoded HTML before applying the allowlist. - Existing rich-text paths that auto-link plain URLs use
sanitize_rich_text|url2link|nl2br. - Keep sanitization display-only unless requirements explicitly say otherwise. Do not sanitize on write or migrate stored rich-text content without discussion.
- Preserve editor round-trips: when adding or changing rich-text render paths,
keep raw stored values available to editors, and ensure any values placed in
HTML attributes or JavaScript are escaped for that context. Do not assume
sanitize_rich_textmakes non-HTML contexts safe. url2linkonly auto-links safehttp/httpsURLs and valid email addresses. Avoid broadening schemes without updating tests and the sanitizer policy.
- Never modify
/database_schema/create-schema.sqldirectly - Create upgrade scripts in
/database_schema/upgrades/ - Follow naming convention:
X.Y.Z.sql(version number) - Use Phing tasks to apply upgrades:
composer buildor phing targets
- Add and update application config definitions in
lib/Config/ConfigKeys.phpand related metadata inlib/Config/ConfigKeysMeta.php. - Keep generated config artifacts in sync by running
composer config-dist:generateandcomposer env-example:generateafter changing config definitions. - Use
composer config-dist:checkandcomposer env-example:checkto verify generated files before committing. - For renamed or removed settings, update the legacy/deprecated key handling instead of silently dropping existing user configuration.
- Translation files in
/lang/{language_code}/ - Each language has separate files for different sections
- Use
translatefunction in templates - Keys use dot notation:
section.subsection.key - Per-language string overrides are supported via
config/lang-overrides.example.php; prefer that path for deployment-specific wording instead of editing tracked language files.
Issue: When running composer install, you may encounter:
Could not authenticate against github.com
Cause: Composer tries to download packages from GitHub without proper authentication tokens in certain environments.
Workarounds:
- Ensure
COMPOSER_AUTHor GitHub tokens are configured in CI - Try
composer install --prefer-distto use cached packages - Check if
composer.lockis up to date and committed - In some cases, the error is intermittent; retry may succeed
- If working on tasks that don't require vendor dependencies, note this limitation and proceed
Prevention: Always commit both composer.json and composer.lock together.
Issue: Template changes not appearing.
Workaround: Clear template cache:
rm -rf tpl_c/*Prevention: In development, ensure proper permissions on tpl_c/ directory.
Issue: PHPStan may run out of memory on large codebases.
Workaround: Increase memory limit:
composer phpstan -- --memory-limit=2GThe CI workflow already uses --memory-limit 2G and has debug fallback.
Issue: Application errors related to file permissions.
Required Permissions:
/tpl_c/- Must be writable by web server/tpl/- Must be writable by web server/uploads/- Must be writable by web server- Configured log directory - Must be writable
Fix:
chmod 755 tpl_c tpl uploads
# Ensure web server user owns these directoriesIssue: Application won't work if PHP session.auto_start is enabled.
Fix: Ensure session.auto_start = 0 in php.ini.
Important: Never modify code in /lib/external/ - these are third-party libraries maintained separately.
- Focus on domain logic and business rules
- Mock dependencies using PHPUnit mocks
- Keep tests fast and isolated
- Test file location should mirror source file structure
- Name test files and test classes after the class under test with a
Testsuffix (for example,Pages/Admin/ManageUsersPage.php->tests/Pages/Admin/ManageUsersPageTest.php) - Tests for PSR-4 code under
src/go intests/src/mirroring the source path, and are namespacedLibreBooking\Tests\...via theautoload-devmapping (for example,src/Application/Authentication/MicrosoftOAuthCallback.php->tests/src/Application/Authentication/MicrosoftOAuthCallbackTest.phpdeclaringLibreBooking\Tests\Application\Authentication\MicrosoftOAuthCallbackTest) - Prefer one primary test file per source class; add extra files only when a clear separation is needed
- Require database setup
- Test actual database interactions
- Located in
/tests/Integration/ - Run separately with
composer test:integration
For integration tests:
- Copy config template:
cp config/config.dist.php config/config.php - Configure test database credentials in config.php
- Run database setup scripts or use Phing
Generate coverage reports:
./vendor/bin/phpunit --coverage-html ./var/Output goes to ./var/ directory.
- Main docs:
/docs/source/(reStructuredText format) - DEVELOPER-README.rst - Developer guide
- API.rst - REST API documentation
- INSTALLATION.rst - Installation instructions
- CONFIGURATION.rst - Configuration guide
- CONTRIBUTING.md - Contributing guidelines
Documentation uses Sphinx and is hosted on ReadTheDocs.
Build locally:
pip install -r requirements-docs.txt
cd docs
sphinx-build -n -W --keep-going -b html source build/htmlOr use tox:
pip install tox
tox -e docsOutput: docs/build/html/
- Markdown (
.md) - Used for: README, CHANGELOG, CONTRIBUTING - reStructuredText (
.rst) - Used for: Technical documentation in/docs/source/
- Never commit secrets - Use config files (ignored in git) for credentials
- SQL injection - Use prepared statements (PDO/mysqli)
- XSS prevention - Escape output in templates
- CSRF protection - Use built-in token system
- Input validation - Validate all user input
- File uploads - Validate file types and sizes
- Admin-authored content - Treat administrator-authored content according
to the trust model in
SECURITY.md, but still harden rich-text/template output as defense in depth where practical - Email template files - Validate template filenames before using them in
filesystem paths; follow the existing
ManageEmailTemplatesPresentervalidation pattern - External links opening in a new tab - Any
target="_blank"link to an external, third-party-controlled origin must includerel="noopener noreferrer"to prevent tabnabbing (the opened page gaining access towindow.opener). Internal/same-origin links (downloads, exports, self-hosted URLs) don't need it.
- Template caching - Smarty templates are compiled and cached in
/tpl_c/ - Database queries - Use proper indexing, avoid N+1 queries
- Session management - Configured in
config.php - Asset optimization - Minify CSS/JS for production
The REST API is in /WebServices/:
- RESTful design principles
- Authentication via API keys or tokens
- Version-aware endpoints
- JSON responses
- See
docs/source/API.rstfor detailed API documentation
Plugins extend LibreBooking functionality:
- Create plugin directory in
/plugins/{PluginType}/ - Implement required interface for plugin type
- Add configuration file:
{PluginName}.config.dist.php - Document plugin usage
- Register plugin in application configuration
- Main Repository: https://github.com/LibreBooking/librebooking
- Live Demo: https://librebooking-demo.fly.dev/
- Discord Community: https://discord.gg/4TGThPtmX8
- Documentation: https://librebooking.readthedocs.io/en/latest/
- Docker Image: https://github.com/LibreBooking/docker
- Issue Tracker: https://github.com/LibreBooking/librebooking/issues
# Setup
composer install # Install dependencies
cp config/config.dist.php config/config.php # Create config
composer preflight # Validate environment
# Development
composer phpcsfixer:fix # Auto-fix code style
composer phpstan # Static analysis
composer phpunit # Run tests
composer test # Run tests + lint
./ci/ci-phplint # PHP syntax check
# Building
composer build # Package release (Phing)
# Documentation
tox -e docs # Build Sphinx docs
./tools/phpdoc # Generate API docs
# Database
composer build -- baseline.db # Create database
composer build -- upgrade.db # Apply upgrades- Unclear requirements - Ask user for clarification
- Breaking existing tests - Investigate why before modifying tests
- Security concerns - Consult before implementing security-sensitive features
- Large architectural changes - Discuss design before implementing
- Database schema changes - Review upgrade process requirements
- Third-party dependencies - Check if really needed before adding
- Read CONTRIBUTING.md before submitting PRs
- Check existing issues before reporting new ones
- Test thoroughly - Both unit and integration tests
- Document changes - Update relevant documentation
- Follow conventions - Consistency is key for maintainability
- Ask questions - Community is helpful on Discord
Version: 1.0 Last Updated: 2026-02-16 Maintainers: LibreBooking Community