alexsoft12 / simple_calendar
Framework-agnostic calendar generator for PHP
v1.0.0
2026-03-26 02:57 UTC
Requires
- php: ^8.2
Requires (Dev)
- phpunit/phpunit: ^12.0
- squizlabs/php_codesniffer: 4.x-dev
README
Framework-agnostic calendar generator for PHP 8.4+.
Screenshot
Highlights
- Pure PHP core
- Immutable fluent API
- Built-in translations loaded from PHP files
- Bootstrap 4 friendly default template
- Fully customizable templates through arrays or JSON
Requirements
- PHP 8.4+
Installation
composer require alexsoft/simple_calendar
Basic Usage
use Simple\Calendar\CalendarGenerator; use Simple\Calendar\DateProvider; use Simple\Calendar\TemplateManager; $calendar = new CalendarGenerator( new TemplateManager(), new DateProvider(), ); $html = $calendar->render(2026, 3);
The package does not ship any CSS. The default template only outputs simple HTML using familiar Bootstrap 4 classes.
Fluent API
use Simple\Calendar\CalendarGenerator; use Simple\Calendar\DateProvider; use Simple\Calendar\Enums\DayFormat; use Simple\Calendar\TemplateManager; $html = (new CalendarGenerator( new TemplateManager(), new DateProvider(), )) ->month(2026, 3) ->bootstrap4() ->dayFormat(DayFormat::Long) ->navigation('https://example.test/reports/daily-sales', true) ->events([ 10 => '<strong>Sales</strong>', 15 => [ [ 'title' => 'Total sales', 'content' => '<table class="table table-sm"><tr><td>100.00</td></tr></table>', 'attributes' => ['class' => 'sale-entry'], ], ], ]) ->render();
Preconfigured Generator
You can configure the generator once and then start a fluent chain from month().
use Simple\Calendar\CalendarConfig; use Simple\Calendar\CalendarGenerator; use Simple\Calendar\DateProvider; use Simple\Calendar\Enums\DayFormat; use Simple\Calendar\Enums\MonthFormat; use Simple\Calendar\TemplateManager; $config = new CalendarConfig( monthFormat: MonthFormat::Long, dayFormat: DayFormat::Long, showNavigation: true, useSegments: true, navigationUrl: 'https://example.test/reports/daily-sales', locale: 'es', eventHtml: '{event}', ); $calendar = new CalendarGenerator( new TemplateManager(), new DateProvider(), $config, ); $html = $calendar ->month(2026, 3) ->events([ 1 => '<strong>Ventas</strong>', ]) ->render();
Templates
Custom templates can be passed as arrays or JSON strings.
Bootstrap 4 Default
$html = (new CalendarGenerator( new TemplateManager(), new DateProvider(), )) ->month(2026, 3) ->render();
Array Template
$template = [ 'table_open' => '<div class="table-responsive"><table class="table table-bordered custom-calendar">', 'heading_row_start' => '<tr>', 'heading_previous_cell' => '<th><a href="{previous_url}"><<</a></th>', 'heading_title_cell' => '<th colspan="{colspan}">{heading}</th>', 'heading_next_cell' => '<th><a href="{next_url}">>></a></th>', 'heading_row_end' => '</tr>', 'week_row_start' => '<tr>', 'week_day_cell' => '<td>{week_day}</td>', 'week_row_end' => '</tr>', 'cal_row_start' => '<tr>', 'cal_cell_start' => '<td data-fulldate="{fulldate}">', 'cal_cell_start_today' => '<td class="today" data-fulldate="{fulldate}">', 'cal_cell_content' => '<div class="day_num">{day}</div><div class="content">{content}</div>', 'cal_cell_content_today' => '<div class="day_num highlight">{day}</div><div class="content">{content}</div>', 'cal_cell_no_content' => '<div class="day_num">{day}</div>', 'cal_cell_no_content_today' => '<div class="day_num highlight">{day}</div>', 'cal_cell_blank' => ' ', 'cal_cell_end' => '</td>', 'cal_cell_end_today' => '</td>', 'cal_row_end' => '</tr>', 'table_close' => '</table></div>', ]; $html = (new CalendarGenerator( new TemplateManager(), new DateProvider(), )) ->month(2026, 3) ->template($template) ->render();
JSON Template
$template = json_encode([ 'table_open' => '<table class="table table-bordered">', 'heading_row_start' => '<tr>', 'heading_previous_cell' => '<th><a href="{previous_url}"><<</a></th>', 'heading_title_cell' => '<th colspan="{colspan}">{heading}</th>', 'heading_next_cell' => '<th><a href="{next_url}">>></a></th>', 'heading_row_end' => '</tr>', 'week_row_start' => '<tr>', 'week_day_cell' => '<td>{week_day}</td>', 'week_row_end' => '</tr>', 'cal_row_start' => '<tr>', 'cal_cell_start' => '<td data-fulldate="{fulldate}">', 'cal_cell_start_today' => '<td data-fulldate="{fulldate}">', 'cal_cell_content' => '<div>{day}</div><div>{content}</div>', 'cal_cell_content_today' => '<div><strong>{day}</strong></div><div>{content}</div>', 'cal_cell_no_content' => '<div>{day}</div>', 'cal_cell_no_content_today' => '<div><strong>{day}</strong></div>', 'cal_cell_blank' => ' ', 'cal_cell_end' => '</td>', 'cal_cell_end_today' => '</td>', 'cal_row_end' => '</tr>', 'table_close' => '</table>', ], JSON_THROW_ON_ERROR);
Events
events() accepts:
CalendarEventsByDayday => htmlday => [[title, content, attributes]]CalendarEventCalendarEventData
$dailySale = [ 5 => "<table class='table table-bordered table-sm'><tr><td>Total</td><td>250.00</td></tr></table>", 9 => [ [ 'title' => 'Total sales', 'content' => '<div>840.50</div>', 'attributes' => ['class' => 'daily-total'], ], [ 'title' => 'Shipping', 'content' => '<div>25.00</div>', ], ], ]; $html = (new CalendarGenerator( new TemplateManager(), new DateProvider(), )) ->month(2026, 3) ->navigation('https://example.test/reports/daily-sales', true) ->events($dailySale) ->render();
Translations
The core ships with PHP file based translations in src/lang.
Included locales:
enes
$html = (new CalendarGenerator( new TemplateManager(), new DateProvider(), )) ->month(2026, 3) ->locale('es') ->render();
You can also point to your own translation directory:
$html = (new CalendarGenerator( new TemplateManager(), new DateProvider(), )) ->month(2026, 3) ->locale('fr', __DIR__ . '/lang') ->render();
Default Markup
The default template uses familiar Bootstrap 4 classes such as:
tabletable-borderedtable-smtable-responsive
The package does not inject styles. If your application does not use Bootstrap, provide your own template.
Security Notes
- Event content is rendered as trusted HTML by design.
- Custom templates are rendered as trusted HTML by design.
- HTML attributes provided through event metadata are normalized and escaped before rendering.
- If event content comes from users, sanitize it before passing it to the package.
- If you use a custom translation directory, treat it as trusted application code because translation files are loaded as PHP.
Testing
vendor/bin/phpunit
License
This project is released under the MIT License.
