lsr / core
Laser framework core.
Requires
- php: >=8.4
- ext-ctype: *
- ext-fileinfo: *
- ext-gettext: *
- ext-mbstring: *
- ext-pdo_sqlite: *
- ext-simplexml: *
- latte/latte: ^3.0
- lsr/caching: ^0.3
- lsr/db: ^0.3
- lsr/interfaces: ^0.3.16
- lsr/logging: ^0.3
- lsr/orm: ^0.3
- lsr/request: ^0.3
- lsr/routing: ^0.5
- lsr/serializer: ^0.3
- nette/di: ^3.2
- nette/php-generator: ^4.1
- vlucas/phpdotenv: ^5.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/extension-installer: ^1.2
- phpstan/phpstan: ^2.0
- phpstan/phpstan-dibi: ^2.0
- phpstan/phpstan-nette: ^2.0
- phpunit/phpunit: ^12
- roave/security-advisories: dev-latest
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-08 14:50:18 UTC
README
lsr/core provides the Laser framework application runtime: Nette dependency injection, HTTP dispatch, sessions, Latte templating, translations and integration with the LSR routing, request, database, cache and ORM packages. Its namespace is Lsr\Core.
Requirements
- PHP
>=8.4. - PHP extensions:
fileinfo,gettext,simplexml,ctype,mbstringandpdo_sqlite. - Nette DI
^3.2, Latte^3.0, PHP dotenv^5.6and Nette PHP Generator^4.1. - LSR interfaces, logging, routing (
^0.5), request, DB, serializer, cache and ORM dependencies; see composer.json for exact constraints and transitive platform requirements. - An application-owned bootstrap and service configuration, writable temporary/cache and log locations, and database/cache configuration appropriate to the application. This is a framework library, not an application skeleton.
Installation
composer require lsr/core
Application integration
The application owns the following pieces of bootstrap configuration:
- Load Composer's autoloader and define the filesystem/environment constants used by the enabled framework components. In particular,
App::setupDi()readsROOT . 'config/services.php'and writes the compiled container underTMP_DIR . 'di/'; these directory constants need trailing separators. tests/bootstrap.php shows the wider set of constants used by the package's test environment, not a production bootstrap to copy verbatim. - Make the application's
config/services.phpreturn an array of service configuration filenames. App::setupDi() loads these files through Nette's compiler. The repository's config/services.php is test-oriented and references application/test files; provide your own list. - Register
Lsr\Core\DI\LsrExtensionin Nette'sextensionssection together with the request, routing, serializer, DB and cache integrations needed by your application. The core extension does not replace their configuration. - Supply the core extension's required
appDirandtempDiroptions. Both must refer to existing directories. Optional configuration coverslatte.tempDir,translations.defaultLang,translations.supportedLanguages,translations.domains,links.modifiers, and HTTP exception/after-response handlers. See the authoritative configuration schema and service definitions.
After the application constants and service configuration are in place, the FPM entry point can finish bootstrapping with:
use Lsr\Core\App; use Lsr\Core\FpmHandler; App::setupDi(); $handler = App::getServiceByType(FpmHandler::class); if (!$handler instanceof FpmHandler) { throw new RuntimeException('The application must register the LSR core extension.'); } $handler->run();
FpmHandler creates the request, invokes the application, handles dispatch-break responses and configured exception handlers, and finishes the response lifecycle. RouteHandler performs controller/handler dispatch. LsrExtension wires the session, translation, link, menu and Latte services; use it as the integration reference rather than manually reproducing the service graph.
Exact-host routing and links
Core 0.5 passes the current request URI host to lsr/routing 0.5. Routes on that exact normalized host take priority over unrestricted routes; unrestricted routes remain the fallback. Hosts are case-insensitive and a terminal DNS dot is ignored. The host constraint does not select a scheme or port. Requests with no URI host only match unrestricted routes.
In application route files, use the router's domain groups and optionally declare aliases after the routes:
$this->domain('league') ->get('/results/{id}', [ResultsController::class, 'show']) ->name('league.results') ->localize('cs') ->localize('en', '/en/results/{id}'); $this->declareDomain('league.example.com', 'league');
Aliases resolve after all route sources load, using one lookup rather than recursive alias expansion. A reference with no declared alias is a literal host, including single-label hosts. Conflicting declarations fail, and alias declarations cannot change after resolution. For manually assembled routers, call resolveDomains() before routing or generating domain-bound links; normal setup()/loadRoutes() performs resolution.
Links\Generator::route('league.results', ['id' => 42], locale: 'en') uses the localized route's resolved domain. Links to another host are absolute; links to the current normalized host remain relative. The current request's scheme and port are preserved, including nonstandard ports: a domain constraint alone never upgrades HTTP to HTTPS or selects a destination-specific port. Configure that policy at the application/proxy layer.
Legacy Generator::getLink('route.name'), getLinkObject('route.name'), and getAbsoluteLink('route.name') also preserve the route domain and continue applying legacy path modifiers. getRouteLink($route) does the same for a route object. App::redirect('route.name') and App::redirect($route) preserve domain destinations; literal strings, URI objects and path arrays retain their previous behavior. The newer route() API selects exact localized variants and substitutes parameters; legacy calls and redirects keep their existing declared-path/modifier behavior rather than selecting a locale automatically.
The generator reads the current application's base URI for each call, so a shared generator can serve successive requests on different hosts without reusing the first request's host. Menu entries configured by route name retain their domain destination and only become active on the appropriate host; serialized named menu items refresh their URL and active state when restored. The Tracy routing panel shows the request host, the selected route's domain and the domain-specific routing trees.
Controller argument metadata caches include the resolved domain, so handlers sharing a method/path on different hosts can have different argument types. Request arguments accept compatible PSR request interfaces as well as the LSR request interface, including the PSR request used by redirect aliases.
Trusted hosts, proxies and deployment
The request URI is the authority for both routing and generated URLs. Validate accepted hosts at the web server or trusted request-factory boundary; routing is not a host allowlist because unrestricted routes deliberately accept other hosts. Only trust forwarded host/scheme headers from explicitly configured proxies. Core does not read forwarded headers itself. Host declarations accept ASCII DNS/punycode names and IP literals, not schemes, paths, credentials, ports or wildcards; convert internationalized names to punycode before configuration.
Deploy core and routing 0.5 together in each consuming application and rebuild its compiled route cache whenever domain declarations or alias targets change. Compiled routes store resolved hosts, so changing environment-specific aliases without rebuilding the cache does not retarget cached routes. The routing 0.5 cache format rejects older cache payloads; use a cache path/version appropriate to each independently deployed application and restart long-running workers after deploying route changes.
Development
CI runs the complete suite on PHP 8.4 and 8.5. Install the PHP extensions listed in .github/workflows/ci.yml, including the package's required extensions and Redis/ZIP for dependencies, then run:
composer install --prefer-dist --no-interaction --no-progress composer cs vendor/bin/phpstan analyse --no-progress vendor/bin/phpunit --no-coverage
The checkout must be writable; tests/bootstrap.php defines the test filesystem constants and creates tests/tmp/. Tests construct their own services and container fixtures rather than booting an application container, so no MySQL or Redis server is needed. composer test and composer phpstan are also available. See phpunit.xml and phpstan.neon.
Run composer cs to check PHP coding style and composer cs:fix (or composer cbf) to apply fixes with PHP CS Fixer. The rules and source paths are defined in .php-cs-fixer.php.
AI coding assistance
See LSR Skills for AI agent skills for working with the LSR framework.
License
Licensed under the MIT License.