diff --git a/README.md b/README.md
index 0dfd82c5..bb85bd48 100644
--- a/README.md
+++ b/README.md
@@ -12,29 +12,4 @@
Ray.Di is DI and AOP framework for PHP inspired by [Google Guice](https://github.com/google/guice/wiki).
-## Binding snapshots
-
-Collect a module's composed bindings explicitly when diagnostics are needed:
-
-```php
-use Ray\Bindings\Bindings;
-
-$bindings = new Bindings();
-$module->accept($bindings);
-
-$markdown = $bindings->toMarkdown();
-$html = $bindings->toHtml($composerLock, 'prod-app', $vendorDir);
-```
-
-The snapshot is captured when `accept()` is called. Later module changes and
-Injector processing do not change it; visiting another module with the same
-`Bindings` instance replaces the snapshot. It contains bindings composed by
-the application module through `bind()`, `install()`, and `override()`, without
-Injector built-ins or bindings discovered later during object resolution.
-
-`Injector` does not create `bindings.md` automatically. Applications that
-previously read that generated file should use `toMarkdown()` or `toHtml()` as
-shown above. The explicit `Ray\Bindings\BindingsMarkdown` file writer and the
-`bindings-html` command remain available for file-based workflows.
-
https://ray-di.github.io
diff --git a/bin/bindings-html b/bin/bindings-html
deleted file mode 100755
index 124266fc..00000000
--- a/bin/bindings-html
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env php
- [composer.lock] [message] > bindings.html
- * bin/bindings-html - [composer.lock] [message] < bindings.md > bindings.html
- *
- * The markdown is embedded verbatim in a
data island; a shared viewer
- * (stylesheet + script served from jsDelivr) decorates it on load: colour-coded
- * events, a type filter, and the discarded side of every collision in red.
- * Without JavaScript the
stays visible as a plain-text fallback */
-#view{display:none}
diff --git a/docs/bindings/bindings.js b/docs/bindings/bindings.js
deleted file mode 100644
index ee3abf5b..00000000
--- a/docs/bindings/bindings.js
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Ray.Di bindings viewer.
- *
- * Decorates a bindings.md embedded as
into the mount point
- *
: colour-coded provenance events, a type filter, and the
- * discarded side of every collision shown in red. Shared across consumers
- * (the bin/bindings-html CLI, documentation generators) via jsDelivr, so the
- * rendering lives in one place. Without this script the
stays visible as
- * a plain-text fallback.
- */
-(function () {
- function run() {
- var src = document.getElementById('src');
- var view = document.getElementById('view');
- if (!src || !view) { return; }
- var text = src.textContent;
-
- var srcmap = [];
- var mapEl = document.getElementById('srcmap');
- if (mapEl) { try { srcmap = JSON.parse(mapEl.textContent) || []; } catch (e) { srcmap = []; } }
-
- function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
-
- // esc for a double-quoted attribute value (esc leaves quotes untouched)
- function attr(s) { return esc(s).replace(/"/g, '"'); }
-
- function resolve(fqcn) {
- // Exact-class overrides (x=1) first — used when two packages share a PSR-4
- // prefix (e.g. bear/package vs bear/aura-router-module both claim BEAR\Package\).
- for (var i = 0; i < srcmap.length; i++) {
- var ex = srcmap[i];
- if (ex.x === 1 && ex.p === fqcn && ex.path && /^https?:\/\//.test(ex.u)) {
- return { gh: ex.u + (ex.u.indexOf('github.com') !== -1 ? '/blob/' + ex.r + '/' + ex.path : ''), local: 'vendor/' + ex.n + '/' + ex.path };
- }
- }
- // Longest PSR-4 prefix wins; equal length keeps the first entry.
- var best = null;
- for (var j = 0; j < srcmap.length; j++) {
- var e = srcmap[j];
- if (e.x === 1) { continue; }
- if (fqcn.indexOf(e.p) === 0 && (!best || e.p.length > best.p.length)) { best = e; }
- }
- // only link http(s) repositories, so a hostile source URL can't become a javascript: href
- if (!best || !/^https?:\/\//.test(best.u)) { return null; }
- var file = (best.d ? best.d + '/' : '') + fqcn.slice(best.p.length).replace(/\\/g, '/') + '.php';
- var gh = best.u.indexOf('github.com') !== -1 ? best.u + '/blob/' + best.r + '/' + file : best.u;
- return { gh: gh, local: 'vendor/' + best.n + '/' + file };
- }
-
- // link a class to its source (href for humans, data-src for agents) showing
- // the given already-escaped text; returns the text unchanged if unresolved
- function link(fqcn, text) {
- var r = resolve(fqcn);
- return r ? '' + text + '' : text;
- }
-
- // esc, but wrap any resolvable class name in a source link
- function escLink(s) {
- var out = '', last = 0, re = /[A-Za-z_][A-Za-z0-9_]*(?:\\[A-Za-z0-9_]+)+/g, m;
- while ((m = re.exec(s)) !== null) {
- out += esc(s.slice(last, m.index)) + link(m[0], esc(m[0]));
- last = m.index + m[0].length;
- }
- return out + esc(s.slice(last));
- }
-
- function section(name) {
- var m = text.match(new RegExp('(?:^|\\n)## ' + name + '\\n([\\s\\S]*?)(?:\\n## |$)'));
- if (!m) { return []; }
- return m[1].split('\n').filter(function (l) { return l.trim() !== ''; });
- }
-
- var summary = '';
- text.split('\n').slice(0, 8).forEach(function (l) {
- if (l.indexOf('bindings ·') !== -1 && summary === '') { summary = l.trim(); }
- });
- var cm = summary.match(/(\d+) bindings . (\d+) modules . (\d+) replaced . (\d+) discarded/);
- var counts = cm ? [cm[1], cm[2], cm[3], cm[4]] : ['?', '?', '?', '?'];
-
- function renderEvent(line) {
- var m = line.match(/^(bind|replace|keep|move)\s+(.*)$/);
- if (!m) { return '
' + esc(line) + '
'; }
- var typ = m[1], body = m[2], extra = '';
- var dm = body.match(/\s\((discarded|replaced) (.*)\)\s*$/);
- if (dm) {
- var kind = dm[1], inner = dm[2];
- body = body.slice(0, dm.index);
- var im = inner.match(/^(.*) @([^ ]+)$/);
- extra = im
- ? '' + kind + ' ' + escLink(im[1])
- + ' @' + escLink(im[2]) + ''
- : '' + escLink(inner) + '';
- }
- var bm = body.match(/^(.*?) => (.*) @([^ ]+)$/);
- var core = bm
- ? '' + escLink(bm[1]) + ' => '
- + '' + escLink(bm[2]) + '@'
- + '' + escLink(bm[3]) + ''
- : '' + esc(body) + '';
- return '
' + typ + ''
- + core + extra + '
';
- }
-
- function renderBinding(line) {
- var m = line.match(/^(.*?) => (.*)$/);
- if (!m) { return '
' + esc(line) + '
'; }
- var target = m[2], aop = '';
- // peel off a trailing " (aop) +method(Interceptor) ..." and group methods by interceptor
- var am = target.match(/ \(aop\)((?: \+\w+\([^)]*\))+)\s*$/);
- if (am) {
- target = target.slice(0, am.index);
- var by = {}, order = [], pair, re = /\+(\w+)\(([^)]*)\)/g;
- while ((pair = re.exec(am[1])) !== null) {
- if (!by[pair[2]]) { by[pair[2]] = []; order.push(pair[2]); }
- by[pair[2]].push(pair[1]);
- }
- aop = '
';
-
- var provEl = document.getElementById('prov');
- view.querySelectorAll('.chip').forEach(function (c) {
- function toggle() {
- var off = c.getAttribute('data-off') === '1';
- c.setAttribute('data-off', off ? '0' : '1');
- provEl.classList.toggle('hide-' + c.getAttribute('data-t'), !off);
- }
-
- c.addEventListener('click', toggle);
- c.addEventListener('keydown', function (e) {
- if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
- });
- });
-
- // reveal the decorated view, retire the raw data island
- src.style.display = 'none';
- view.style.display = 'block';
- }
-
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', run);
- } else {
- run();
- }
-})();
diff --git a/phpstan.neon b/phpstan.neon
index a969ca10..281d6e3d 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -3,9 +3,7 @@ parameters:
level: max
paths:
- src/di
- - src-bindings
- tests/di
- - tests-bindings
- tests/type
excludePaths:
- tests/tmp/*
@@ -17,5 +15,4 @@ parameters:
ignoreErrors:
- '#contains generic class ReflectionAttribute but does not specify its types:#'
- '#generic class ReflectionAttribute but does not specify its types:#'
- - '#Offset 1 might not exist on array\{\}\|array\{non-falsy-string, string\}#'
- '#will always evaluate to true#'
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 65d808d4..32321c12 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -8,13 +8,11 @@
src/di
- src-bindingstests/di/
- tests-bindingstests-php8
diff --git a/psalm.xml b/psalm.xml
index 47611cfb..4e1ca574 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -8,7 +8,6 @@
>
-
diff --git a/src-bindings/Bindings.php b/src-bindings/Bindings.php
deleted file mode 100644
index 57b2f14f..00000000
--- a/src-bindings/Bindings.php
+++ /dev/null
@@ -1,38 +0,0 @@
-markdown = (new BindingsMarkdown())->render($container);
- }
-
- public function toMarkdown(): string
- {
- return $this->getMarkdown();
- }
-
- public function toHtml(string $composerLock = '', string $message = '', string $vendorDir = ''): string
- {
- return (new BindingsHtml())->page($this->getMarkdown(), $composerLock, $message, $vendorDir);
- }
-
- private function getMarkdown(): string
- {
- if ($this->markdown === null) {
- throw new BindingsNotCollected();
- }
-
- return $this->markdown;
- }
-}
diff --git a/src-bindings/BindingsHtml.php b/src-bindings/BindingsHtml.php
deleted file mode 100644
index 95240a24..00000000
--- a/src-bindings/BindingsHtml.php
+++ /dev/null
@@ -1,308 +0,0 @@
- data island; a small viewer
- * script decorates it on load — colour-coded events, a type filter, and the
- * discarded side of every collision shown in red. The stylesheet and script
- * are shared assets served from a CDN, so every consumer — the bin/bindings-html
- * CLI, a documentation generator, a hand-written page — reuses the same viewer
- * instead of embedding its own copy.
- *
- * Given a composer.lock the viewer also links each class to its source: the
- * lock's per-package source URL + reference resolve to a GitHub blob URL (for
- * humans), and the package name resolves to the local vendor/ path (carried in
- * data-src, so an agent working in the project reads the file directly instead
- * of fetching over the network). Resolution is pure string work — no reflection,
- * no vendor/ access is required for the basic map; when $vendorDir is given,
- * shared PSR-4 prefixes (e.g. bear/package and bear/aura-router-module both
- * claim BEAR\Package\) are disambiguated by checking which package actually
- * contains each class file mentioned in the markdown.
- *
- * {@see fragment()} is the reusable core (the data island, the mount point, and
- * the optional source map) for embedding in a page that owns its own ;
- * {@see page()} wraps it in a standalone document that links the CDN assets.
- * Without JavaScript the
data island, the mount point
- * the viewer decorates, and — when a composer.lock is given — the source
- * map it links classes with. Embed this in a page that references
- * {@see CSS_URL} and {@see JS_URL}.
- *
- * @param non-empty-string|'' $vendorDir Absolute path to composer vendor/ for prefix disambiguation
- */
- public function fragment(string $bindingsMarkdown, string $composerLock = '', string $vendorDir = ''): string
- {
- $md = htmlspecialchars($bindingsMarkdown, ENT_QUOTES, 'UTF-8');
-
- return '
' . $md . '
' . "\n" . ''
- . $this->sourceMap($bindingsMarkdown, $composerLock, $vendorDir);
- }
-
- /**
- * A standalone page around {@see fragment()}, linking the shared viewer
- * assets from the CDN — used by bin/bindings-html.
- *
- * The optional $message renders as a subtitle, e.g. the context the
- * bindings were composed in ("prod-app").
- *
- * @param non-empty-string|'' $vendorDir Absolute path to composer vendor/ for prefix disambiguation
- */
- public function page(string $bindingsMarkdown, string $composerLock = '', string $message = '', string $vendorDir = ''): string
- {
- $fragment = $this->fragment($bindingsMarkdown, $composerLock, $vendorDir);
- $sub = $message === ''
- ? ''
- : "\n