The output buffer is started as soon as the plugin file is included, that is before bootstrap(). The buffer callback returns the page unchanged as long as no link tags were prepared, and catches errors (entry in the PHP error log, page is preserved).
ProductionContextDetector decides from constants and $_SERVER['SCRIPT_NAME'], not from the URL path.
| Context | Condition |
|---|---|
| staff | Constant OSTSCPINC defined or SCRIPT_NAME contains /scp/ |
| client | Constant OSTCLIENTINC defined or SCRIPT_NAME contains neither /scp/ nor /api/ and ends in .php |
| none | all other cases, for example /api/ or a call without a script name |
The staff check takes precedence over the client check.
FilesystemCssDiscovery reads assets/custom/css/*.css with glob() (alphabetical) and checks every file:
realpath() must resolve, otherwise the file (for example a broken symlink) is skipped.^[a-zA-Z0-9][a-zA-Z0-9_-]*\.css$, otherwise it is blocked and noted./staff/i and /client/i are checked in this order, the first match wins.HtmlCssRenderer checks the file name a second time, appends ?v=<modification time> (only when the time is greater than 0), escapes the URL with htmlspecialchars() and produces <link rel="stylesheet" href="...">. The base URL is ROOT_PATH plus assets/custom/css.
OutputBufferInjectionStrategy looks for the first </head> (case-insensitive) and inserts the block directly before it. If </head> is missing or there are no link tags, the page stays unchanged.
| Class | Responsibility |
|---|---|
CustomCssLoaderPlugin |
Plugin class: bootstrap(), enable(), version check, folder and demo files |
CustomCssLoaderConfig |
Configuration fields (config.php) |
CssLoaderOrchestrator |
prepare() collects the link tags, injectIntoBuffer() inserts them |
ProductionContextDetector |
Detect staff or client |
FilesystemCssDiscovery |
Find and check files |
HtmlCssRenderer |
Produce link tags |
OutputBufferInjectionStrategy |
Insert tags into the buffer |
There are also test helpers (TestContextDetector) and older methods in the plugin class marked as deprecated, which are kept for the unit tests.
On bootstrap() the plugin calls checkVersion(). If installed_version is missing or lower than the version in plugin.php, performUpdate() ensures the CSS folder exists and stores the new version. Demo files are not copied during that.
custom-css-loader/
βββ plugin.php
βββ class.CustomCssLoaderPlugin.php
βββ config.php
βββ composer.json
βββ src/
β βββ CssLoaderOrchestrator.php
β βββ Context/
β βββ Discovery/
β βββ Rendering/
β βββ Injection/
βββ assets/demo/
βββ vendor/ # only in the release ZIP or after composer install
βββ tests/ # PHPUnit