An application directory can be read-only - a container image, a Phar archive - so the entries take a
write directory. Without APP_WRITE_DIR nothing changes: the application writes under its own var/.
Added
APP_WRITE_DIRon the entries:public/index.php,bin/app.php,bin/page.phpandbin/compile.phpread it and pass it on (#146)
Changed
Injector::getInstance($context, $writeDir)delegates toBEAR\Package\Injector; the$tmpDirand$logDirarguments are gone (#146)Bootstrap::__invoke()takes the write directory as a fourth argument (#146)- Requires PHP
^8.2, the floorbear/packagealready had, andbear/package^1.22(#146) bin/compile.phploads.compile.phpwhen it is present (#141)
Fixed
src/Install.phpinjectsphp bin/compile.php prod-appas thecompilescript into created projects, instead of the deprecatedvendor/bin/bear.compile
Additional Notes
This section was added later (2026-08-17).
Upgrading an existing project
A project created from an earlier skeleton holds its own copies of these files, so this release does not
reach it on composer update. Four hunks, the last one once per entry script:
// src/Injector.php
- public static function getInstance(string $context, string|null $tmpDir = null, string|null $logDir = null): InjectorInterface
- {
- $meta = new Meta(__NAMESPACE__, $context, dirname(__DIR__), $tmpDir, $logDir);
- $cacheNamespace = str_replace('/', '_', $meta->appDir) . $context;
- $cache = (new LocalCacheProvider($meta->tmpDir . '/injector', $cacheNamespace))->get();
-
- return PackageInjector::getInstance($meta, $context, $cache);
- }
+ public static function getInstance(string $context, string|null $writeDir = null): InjectorInterface
+ {
+ return PackageInjector::getInstance(__NAMESPACE__, $context, dirname(__DIR__), null, $writeDir);
+ } // src/Bootstrap.php
- public function __invoke(string $context, array $globals, array $server): int
+ public function __invoke(string $context, array $globals, array $server, string|null $writeDir = null): int
{
- $app = Injector::getInstance($context)->getInstance(AppInterface::class);
+ $app = Injector::getInstance($context, $writeDir)->getInstance(AppInterface::class); // public/index.php, bin/app.php, bin/page.php - one line each
-exit((new Bootstrap())($context, $GLOBALS, $_SERVER));
+exit((new Bootstrap())($context, $GLOBALS, $_SERVER, getenv('APP_WRITE_DIR') ?: null)); // bin/compile.php - the build and the boot must derive the same paths
-exit(Compiler::fromInjector(Injector::getInstance($context), $context)());
+$writeDir = getenv('APP_WRITE_DIR') ?: null;
+
+exit(Compiler::fromInjector(Injector::getInstance($context, $writeDir), $context, $writeDir)());composer.json asks for "php": "^8.2" and "bear/package": "^1.22".
Verify both layouts:
APP_WRITE_DIR=/tmp/write php bin/page.php get /
# 200 OK, and /tmp/write/{Vendor}/{Project}/{context}/{tmp,log} exists
php bin/page.php get /
# 200 OK, and var/tmp/{context} is used as beforeA project that keeps its own Injector shape can take the $writeDir argument alone: everything else here
only carries it from the entry to that call.