Skip to content

1.16.0

Latest

Choose a tag to compare

@koriym koriym released this 16 Aug 18:19
· 5 commits to 1.x since this release

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_DIR on the entries: public/index.php, bin/app.php, bin/page.php and bin/compile.php read it and pass it on (#146)

Changed

  • Injector::getInstance($context, $writeDir) delegates to BEAR\Package\Injector; the $tmpDir and $logDir arguments are gone (#146)
  • Bootstrap::__invoke() takes the write directory as a fourth argument (#146)
  • Requires PHP ^8.2, the floor bear/package already had, and bear/package ^1.22 (#146)
  • bin/compile.php loads .compile.php when it is present (#141)

Fixed

  • src/Install.php injects php bin/compile.php prod-app as the compile script into created projects, instead of the deprecated vendor/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 before

A 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.