| di | by dg | removed Statement compatibility shims from expression classes WIP | | | |
| di | by dg | deprecated magic properties (BC break) | | | |
| di | by dg | removed compatibility for old class names | | | |
| di | by dg | removed Definition::generateMethod() (BC break) | | | |
| di | by dg | removed deprecated stuff - %parameters% - three ... dots - @inject & @var | | | |
| di | by dg | opened 4.0-dev | | | |
| di | by dg | ParametersExtension: nested dynamic parameters are addressed by key paths, dotted strings keep their flat meaning (BC) setDynamicParameterNames() entries are now shape-driven: a string stands for itself (a plain top-level name in which a dot has no special meaning, as before), a list of keys addresses a nested value. DynamicValue markers resolve to key paths as well. This removes the reinterpretation of user-supplied dotted names introduced with the dotted-name feature, at both places where it happened: the marker literal injection (getRef over exploded name) and the config default lookup in the generated fallback. The runtime name of a path is its dot-joined form; the dot-in-key guard moved to the single name<->path mapping point and newly covers explicit paths too. Claude-Session: https://claude.ai/code/session_01HcUe1zbekTPjU5LuCkxgFT | | | |
| di | by dg | ParametersExtension: dynamic parameters via DynamicValue marker and dotted names WIP setDynamicParameterNames() now accepts dotted names (e.g. 'db.password'), marking a value nested inside a parameter as dynamic; the marker literal is injected at that position and the promoted top-level key regenerates its subtree at runtime, so sibling values stay compiled in. Adds Nette\DI\DynamicValue, an inline marker usable directly in the parameters config at any depth; the extension derives its dotted name from its position. Its optional value is the default used when no runtime value is supplied. The value is excluded from serialization so a per-request value (e.g. from nette/bootstrap) cannot bust the cache. | | | |
| di | by dg | Definitions: the previous-generation API is deprecated Deprecated in favour of the canonical vocabulary (removal planned for 4.0): - addDefinition() and add*Definition() -> add() with factory()/accessor()/locator()/imported() - getDefinition()/getDefinitions()/hasDefinition()/removeDefinition() -> get()/getAll()/has()/remove() - getByType() -> has(type:) or get(type:)->getName() - getDefinitionByType() -> get(type:) - findByType()/findByTag() -> find(type:)/find(tag:) with Definition::getTag() - literal() -> Nette\DI\code() - addExcludedClasses() -> excludeFromAutowiring() - addTag()/setAutowired() -> tag()/autowired() - setFactory()/getFactory() -> setCreator()/getCreator() - addSetup() -> setup(); setSetup() -> clearSetup() and setup() - getImplement() -> getType() The canonical methods no longer route through the deprecated ones: the lookup primitives moved to private lookupByName()/lookupByType()/findAllByType()/ hasName()/removeByName(). The remaining internal uses of the old accessors (Resolver, ServicesExtension, DefinitionSchema) are the compilation machinery these wrappers delegate to; they become internal in 4.0. | | | |
| di | by dg | Definition::getName() returns string (BC break) [WIP] | | | |
| di | by dg | FactoryDefinition API | | | |
| di | by dg | Definitions: the canonical decade API ContainerBuilder / Nette\DI\Definitions: - add() takes an optional type: (mirrors NEON's create:/type: sibling keys) and preserves the concrete definition class via @template; type: cannot be combined with a ready-made definition - has() addresses by tag: too; get()/find()/has()/remove() complete the addressing matrix (get/remove take unique addresses, find multi ones, has any) - a backslashed positional name in get()/has() throws an instructive error instead of silently failing (a service name never contains a backslash) - excludeFromAutowiring() supersedes addExcludedClasses() - the registration primitive moved to private registerDefinition(); public addDefinition() and the four add*Definition() helpers now delegate to it Definitions: - getName() returns non-nullable string (the name is an invariant of registration); the compilation machinery distinguishes unregistered definitions (e.g. factory result definitions) via the new @internal tryGetName() - exported() replaces the never-released setExported() - the canonical verbs own the bodies: tag(), autowired(), setup(); the old names delegate to them - FactoryDefinition gained setCreator()/setup()/setArguments() delegating to the result definition (mirrors NEON, where create:/setup:/arguments: on a factory configure the produced service, while tags:/autowired: belong to the factory) Nette\DI functions: - factory(), accessor(), locator() build the concrete generated-definition kinds (typed, one word = one meaning); implement() remains as the shape-detecting dispatcher for runtime-unknown interfaces | | | |
| di | by dg | CompilerExtension: onStartup() adds DSL expressions / code to initialize() onStartup(Expression|string $code, array $args = []) is the clean, first-class way to add startup code to the container's initialize() method - a full replacement for the verbose $this->initialization->addBody($builder-> formatPhp(...)) dance. It accepts a DSL expression (e.g. service('session')->method('start')) or a PHP statement string with ? placeholders whose args may themselves be DSL expressions; both are completed and generated via ContainerBuilder::formatPhp(), which is un-deprecated as the DI-aware statement formatter behind it. Built-in DIExtension and ParametersExtension migrated to onStartup() as reference examples. This is the CompilerExtension-level onStartup (for extensions), distinct from the deferred Definitions-level one (for config closures). | | | |
| di | by dg | Extensions: rewritten to the Definitions DSL (DIExtension, DecoratorExtension, InjectExtension, SearchExtension) | | | |
| di | by dg | Compiler: journal stamps mutations with the current actor (from Schedule) Schedule now tracks the currently-running actor; the Compiler sets it around each hook (the extension's name) and around the config closures ('config'), and the ContainerBuilder's fan-out pulls it when recording a mutation. Each journal entry thus carries who made the change, giving a real biography ("made: created by 'maker', setup added by 'deco'"), with Journal::getCreator() for who registered a service. CompilerExtension gets getName(). Blanket enrichment of every exception message with the origin was left out - it adds noise to the common case; the actor is available programmatically for targeted use (e.g. a future Tracy panel). | | | |
| di | by dg | added Nette\DI\Compiler\Journal: mutation events from definitions The single notifier on Definition is generalized from "type/autowired changed" into a mutation event carrying (definition, action, value), and every mutation primitive reports through it (setType/setCreator/setArguments/ addSetup/setSetup/addTag/removeTag/setTags/setAutowired/lazy/clearSetup) - so it catches the new DSL verbs, the legacy verbs and direct access alike. ContainerBuilder fans the stream out to two consumers: the existing needsResolve lock (reacts only to type/autowired) and the new Journal, an append-only biography of each service (getJournal()/getBiography()). Silent during resolve(), where mutations are internal autowiring, not "what an extension did". add() now registers before setting the creator so the creator is journaled. The actor stamp comes in the next commit. | | | |
| di | by dg | Config: a PHP config file can return a closure operating on Definitions The DSL becomes reachable from a real config file: return function (Definitions $di) { ... } and use the full vocabulary bare (namespace Nette\DI). PhpAdapter wraps a returned closure into an internal @closures section (so it flows through the array-based Loader contract untouched); the Compiler runs those closures first, at load-time, so their add() is immediate and precedes the extensions, while hook()/ remove() reaching framework services defer into a phase (ADR 0006). The @closures section is exempt from the unknown-section check; every other unknown section still errors. | | | |
| di | by dg | added Nette\DI\Definitions, the config-facing view of ContainerBuilder The narrow interface a PHP config closure receives and an extension can type against: the immediate vocabulary (add/remove/get/find/has/getAll) plus hook(), the only time machine (ADR 0006). It lives in one namespace with the element functions so config files use everything bare, and it deliberately hides the internal compilation machinery (resolve/complete/ generateCode) and the legacy names. ContainerBuilder implements it; the of: generics carry through, so typing via Definitions matches the builder. parameters()/onStartup() are omitted for now and add additively later. | | | |
| di | by dg | ContainerBuilder: remove() (immediate) + teaching timeline errors remove(name/type) drops an existing service immediately (ADR 0006: the whole vocabulary is immediate, no hidden deferral), so the intuitive if (has()) { remove(); add(); } just works. It reuses get(of: Definition) for addressing and the missing-service error. To drop a service an extension registers, use hook(Phase::Discover, ..., after: '*'). get()/remove() now teach the compilation timeline: while services may still be registered (before the Discover phase completes, known via the schedule), a "service not found" error gains a hint pointing at hook(Phase::Modify, ...). A standalone builder without a schedule keeps the plain error. | | | |
| di | by dg | added Nette\DI\Compiler\Schedule; ContainerBuilder::hook() defers into a phase Introduces the Nette\DI\Compiler namespace (Latte-style; new engine classes are born there, existing ones move in 4.0) with its first inhabitant, Schedule: the compilation schedule that owns the hook registry, their before/after ordering and the phase state (which phase drains, which have completed), and fails loudly on hooks that would never run. The Compiler owns a Schedule and shares it with the ContainerBuilder (builder -> Schedule <- Compiler, no builder<->Compiler cycle); Compiler::addHook() and the running-phase / Setup-constraint guards move onto it, plus a new completed-phase guard (scheduling into a phase that already ran throws). The Register double drain keeps working: it is marked completed only after the second drain. ContainerBuilder::hook(phase, fn, before, after) is the DSL time machine (ADR 0006): the only way to reach services registered later. It accepts only Register/Discover/Modify (Setup is extension-level, Compile leaks the generated ClassType); the callback receives the builder. A standalone builder with no Compiler throws an instructive error. | | | |
| di | by dg | ServiceDefinition: $lazy property replaced by lazy()/isLazy() (BC break) The public $lazy property is now private; set it via lazy() and read it via the new isLazy(): ?bool. Built-in extensions (DI, Services) updated accordingly. BC break: code writing $def->lazy = ... or reading $def->lazy must switch to $def->lazy(...) / $def->isLazy(). | | | |
| di | by dg | ContainerBuilder: add() registers a service from an expression or definition add(name, creator) is the DSL registration verb: a ready-made definition (from implement()/imported()) is registered as-is, anything else becomes a ServiceDefinition whose creator is built via the same create() dispatch (bare class -> new, callable -> factory method, expression -> passthrough). It returns the real definition for fluent configuration and errors on a duplicate name. Adds the implement() (factory/accessor/locator by interface shape) and imported() element functions. | | | |
| di | by dg | ContainerBuilder: retrieval API get(), find(), has(), getAll() Short, consistent config-facing accessors addressing services by name or by type:/tag:, mirroring add/remove. get() returns one definition (throwing on 0/many for a type), find() returns definitions (find(tag:) gives definitions, not tag values like findByTag()), has() is a bool, getAll() aliases getDefinitions(). get()/find() take an of: parameter (ADR 0005): get() asserts the result is of that definition class (ServiceDefinition by default, the common case) so the 99% path is friction-free and the rest gets an instructive error; find(of:) filters the result set. Conditional return types keep static analysis exact without a PHPStan extension. | | | |
| di | by dg | Definitions: fluent verbs setup(), tag(), autowired(), lazy() (old names stay) Adds the definition-handle side of the two-object split from dsl-proposal.md. Definition base gets tag()/removeTag()/autowired(); ServiceDefinition gets setup() (the '$prop'/'$prop[]'/method mini-language, or an expression step), clearSetup() and lazy(). The old verbs (addSetup, addTag, setAutowired) stay untouched; deprecation is a 4.0 concern. call() lives only on expressions and setup only on definitions, so the split is structurally enforced. | | | |
| di | by dg | added element functions (service, wire, create, param, ...) First wave of the DSL value builders from dsl-proposal.md: pure, context-free factories for Expression nodes, living in one namespace with the Definitions type so config files can use them bare. service()/wire()/ self() build references, param()/expand() build parameter expansions, create()/call() dispatch by shape (bare class -> new, callable -> factory method), services()/cast()/not()/code() wrap the remaining nodes. No string decoding: @service and %param% stay literal. | | | |
| di | by dg | ArgumentPlaceholder::Single marks an open argument position for autowiring In an Instantiation or Call, an ArgumentPlaceholder::Single argument now means "autowire this position" (as if the argument was omitted); it is stripped before autowiring so later positional arguments keep their place, mirroring the NEON `_` behaviour. In a PartialCall it stays a runtime placeholder as before. This is what the DSL wire() without arguments will produce. | | | |
| di | by dg | Expressions: fluent method(), property() and constant() for chaining Adds the expression side of the two-object split from dsl-proposal.md as the Chaining trait, used only by the nodes whose value is an object: Reference, Instantiation and Call. Deliberately NOT on the Expression base class - value expressions (casts, parameter expansions, service collections) have nothing to chain on and must not offer it; the base carries only the compilation lifecycle. PartialCall stays out as well: chaining after a first-class callable is a NEON-only edge case. Each method returns the matching node, so chains like service('a')->method('getUrl')->method('getHost') build the AST directly. | | | |
| di | by dg | PartialCall supports argument placeholders (PHP 8.6 partial application) | | | |
| di | by dg | added expression classes (Call, Instantiation, ConstantFetch, Expansion, PropertyAccess, ServiceCollection, SpecialFunction, PhpCode) | | | |
| di | by dg | added PartialCall, expression representing PHP first-class callable syntax | | | |
| di | by dg | ServiceDefinition: creator can be any Expression enables first-class callable in 'create:' | | | |
| di | by dg | Expression::transformValues() used by Helpers to traverse expressions | | | |