Skip to content

1.18.0

Latest

Choose a tag to compare

@Chemaclass Chemaclass released this 20 Jul 05:34
1.18.0
d4f9187

Added

  • Framework lifecycle events, zero-cost when nothing listens: GacelaBootstrapStarted/GacelaBootstrapFinished, ConfigInitialized/ConfigKeyRead/ConfigKeyNotFound, ServiceResolved (once per id), BindingRegistered, ProviderRegistered, CacheCleared, CacheWarmed
  • Gacela\Framework\Testing\GacelaTestCase: bootstrap isolation per test, config overrides, and event-backed assertions — assertServiceResolved(), assertBindingRegistered(), recordedGacelaEventsOf()
  • Typed config accessors on Config/AbstractConfig: getString(), getInt(), getFloat(), getBool(), getArray() — concrete return types, null default means required, wrong type throws instead of coercing (int→float widening allowed); faster than get() + a manual cast
  • Scalar contextual bindings: $config->when(X)->needs('$paramName')->give(30)
  • ArrayAccess on the main container: $container[Id::class], isset, assignment, unset
  • debug:module <Name>: resolved Facade/Factory/Config/Provider, bindings, and dependency tree (--json, --tree)
  • debug:graph: whole-app module dependency graph (--format=text|mermaid|graphviz|json)
  • make:module --template=service [--with-tests]: scaffolds a module that runs out of the box — four pillars plus a Domain service, optionally with a GacelaTestCase-based facade test
  • CrossModuleViaFacadeRule: optional sharedNamespaces allowlist for shared kernels

Changed

  • Bumped gacela-project/container to ^0.10.0. It fixes transient resolutions sharing child instances; uncached construction gets slower (sub-microsecond per resolve), while Gacela's steady-state paths are unaffected because resolved classes are instance-cached
  • Event dispatch is zero-cost when nothing listens (~20% faster warm resolves). BC note: custom EventDispatcherInterface implementations must add hasListeners()

Fixed

  • APP_ENV is read from a single source for both the env-suffixed config files and the merged-config cache key, so they can no longer disagree mid-process
  • ConfigLoader's read cache is keyed by reader and path: the same file under two readers no longer shares a cache entry
  • Gacela::resetCache() also clears the glob cache, so config files added or removed on disk are seen by the next bootstrap in the same process
  • ProviderRegisteredEvent no longer fires twice for a modern AbstractProvider
  • Config::getEventDispatcher() returns a no-op dispatcher before bootstrap instead of throwing
  • Re-bootstrapping rebuilds the event dispatcher; listeners from a previous bootstrap no longer leak
  • The merged-config cache filename embeds an app-root hash: apps sharing a cache directory no longer read each other's config (old files are ignored and removed by cache:clear)

Documentation

  • New docs/events.md (dispatch model, event catalog, listener cookbook) and docs/testing.md (GacelaTestCase, ContainerFixture)
  • Module boundaries in docs/static-analysis.md, config precedence in docs/getting-started.md, scalar bindings and the #[Singleton]/#[Factory] attributes in docs/container-configuration.md