postId = $postId; $bound->references = null; $bound->placeholders = null; $bound->cleaner = null; $bound->attrWriter = null; $bound->composer = null; return $bound; } /** * The one checker of $variable(…)$ values of this composition, with the entry's live map (S22, S31). * * @internal Public for tests; code uses cleaner() / composer(). */ public function references(): VariableRef { return $this->references ??= new VariableRef( $this->design->read(), $this->postId === null ? null : $this->dynamicContent->forPost($this->postId) ); } /** * The one Placeholders instance of this composition (ruling S31). * * @internal Public for tests; code uses attrWriter() / composer(). */ public function placeholders(): Placeholders { return $this->placeholders ??= new Placeholders($this->design->read()); } /** The one choke point of this composition (ValueCleaner, rulings S1–S3, S7, S13, S28, S32). */ public function cleaner(): ValueCleaner { return $this->cleaner ??= new ValueCleaner($this->modules->catalog(), $this->references(), $this->sanitizer); } public function attrWriter(): AttrWriter { return $this->attrWriter ??= new AttrWriter($this->modules->catalog(), $this->placeholders(), $this->cleaner()); } public function composer(): Composer { return $this->composer ??= new Composer( $this->modules->catalog(), $this->rules(), new ShortformWriter($this->placeholders(), $this->media, $this->cleaner()), $this->attrWriter(), $this->cleaner(), $this->design->read(), $this->modules->builderVersion() ); } /** @internal Public for tests; code uses composer() / editor(). */ public function rules(): StructureRules { return new StructureRules($this->modules->catalog()); } public function editor(): TreeEditor { return new TreeEditor($this->rules()); } public function warnings(): Warnings { return new Warnings($this->design->read()); } }