From 851fc4e2d4e48acef99a9ad104bb289fd2e6e9df Mon Sep 17 00:00:00 2001 From: Antonio Ramirez Date: Thu, 28 May 2026 06:38:12 +0200 Subject: [PATCH] re #96 add @template generics to univeros/structure collections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Genericises the whole collection library as one coherent, PHPDoc-only, runtime-safe refactor. Baseline 325 -> 111 entries (-214) — the bulk of the level-6 burn-down. @template scheme (interface and concrete mirror each other): - CollectionInterface extends Traversable - MapInterface; SetInterface, SequenceInterface, VectorInterface, Stack/Queue extend CollectionInterface; PairInterface; PriorityNodeInterface - concrete classes add @implements + pin SPL generics (@implements IteratorAggregate<...> / ArrayAccess<...>); Pair's promoted $key/$value get @var TKey/@var TValue (native `mixed` unchanged) - traits carry their own @template and @use the base trait with bound args; $internal typed per storage shape (array for sequences, array> for Map) Object-key correctness: Map permits arbitrary (incl. object) keys, so every toArray()/jsonSerialize() returning a PHP array is typed array (not array); TKey flows only through iteration. PHPDoc-only: no native type or native property-type changes (avoids the uninitialised-property class of bug). The one runtime touch is a behaviour-neutral `$values ?? []` guard in Set::__construct to satisfy template inference on the spread. Four documented @phpstan-ignore line-suppressions remain in CollectionTrait: the shared trait is used by collections with different storage shapes, so the `$internal = []` default can't match every narrowed @var and the capacity/append paths are unreachable for the delegate-backed collections. Each is annotated with the reason; they mask no real bug (verified with reportUnmatchedIgnoredErrors). Consumer impact: generic base types mean subclasses/users must now specify args, so ~28 new "does not specify its types" findings appear in Cookie, Container, Http, Courier etc. — net still -214. Those are a clean mechanical follow-up (declare @extends Map<...> on the consumer collections, which also clears the MapInterface-return findings left by earlier chunks). Rector then applied configured-set cleanups unlocked by the new types (useless @return removal, return-type inference). composer stan / cs / rector --dry-run green; full suite at the 5 pre-existing ext-mongodb environmental errors; 2724 Structure tests pass. --- benchmarks/tokens-to-ship/README.md | 78 + .../results/usage-log.sample.json | 13 + benchmarks/tokens-to-ship/score.php | 203 +++ benchmarks/tokens-to-ship/task.md | 56 + docs/benchmarks/tokens-to-ship.md | 143 ++ docs/marketing/positioning.md | 135 ++ docs/strategy/ai-driven-strategy.md | 175 ++ phpstan-baseline.neon | 1424 ++--------------- .../Contracts/CollectionInterface.php | 11 + .../Structure/Contracts/HashableInterface.php | 2 +- .../Structure/Contracts/MapInterface.php | 62 +- .../Structure/Contracts/PairInterface.php | 10 +- .../Contracts/PriorityNodeInterface.php | 2 + .../Structure/Contracts/QueueInterface.php | 11 +- .../Structure/Contracts/SequenceInterface.php | 47 +- .../Structure/Contracts/SetInterface.php | 42 +- .../Structure/Contracts/StackInterface.php | 11 +- .../Structure/Contracts/VectorInterface.php | 5 + src/Altair/Structure/Deque.php | 10 + src/Altair/Structure/Map.php | 122 +- src/Altair/Structure/Pair.php | 24 +- src/Altair/Structure/PriorityNode.php | 6 + src/Altair/Structure/PriorityQueue.php | 36 +- src/Altair/Structure/Queue.php | 39 +- src/Altair/Structure/Set.php | 77 +- src/Altair/Structure/Stack.php | 38 +- .../Structure/Traits/CollectionTrait.php | 32 +- src/Altair/Structure/Traits/SequenceTrait.php | 78 +- src/Altair/Structure/Vector.php | 8 +- 29 files changed, 1584 insertions(+), 1316 deletions(-) create mode 100644 benchmarks/tokens-to-ship/README.md create mode 100644 benchmarks/tokens-to-ship/results/usage-log.sample.json create mode 100644 benchmarks/tokens-to-ship/score.php create mode 100644 benchmarks/tokens-to-ship/task.md create mode 100644 docs/benchmarks/tokens-to-ship.md create mode 100644 docs/marketing/positioning.md create mode 100644 docs/strategy/ai-driven-strategy.md diff --git a/benchmarks/tokens-to-ship/README.md b/benchmarks/tokens-to-ship/README.md new file mode 100644 index 00000000..280d85fb --- /dev/null +++ b/benchmarks/tokens-to-ship/README.md @@ -0,0 +1,78 @@ +# Tokens-to-Ship benchmark harness + +Measures the agent tokens, turns, and wallclock to take the frozen +[`task.md`](task.md) from a cold prompt to a passing acceptance suite, on two +arms: **Altair** vs. a **conventional baseline**. + +Read [`docs/benchmarks/tokens-to-ship.md`](../../docs/benchmarks/tokens-to-ship.md) +for the methodology and the honesty guardrails. This README is the operational +"how to run it." + +## Layout + +``` +benchmarks/tokens-to-ship/ +├── task.md # the frozen task + acceptance criteria (the contract) +├── README.md # this file +├── score.php # aggregates a usage log -> results/results.json + table +├── acceptance/ # the external suite both arms must pass (you provide) +├── arms/ +│ ├── altair/ # starting fixture for the Altair arm +│ └── baseline/ # starting fixture for the baseline arm +└── results/ + ├── usage-log.sample.json # synthetic example so the scorer runs out-of-the-box + └── results.json # generated by score.php (real runs go here) +``` + +## Protocol (summary) + +1. **Prepare both arms identically.** `composer install`, DB up, etc. — done + *before* measurement starts and excluded from the token count. +2. **Run the agent** on each arm with the same model, settings, and tool budget. + Preferred: drive it with the Claude Agent SDK so usage is captured per turn. + Fallback: run interactively and export the transcript. +3. **Stop** each run when `acceptance/` passes (the harness decides, not the + agent). Record the run as one entry in `results/usage-log.json`. +4. **Repeat** N ≥ 5 times per arm. Cold start each time. +5. **Score:** `php score.php results/usage-log.json`. + +## Usage-log format + +`results/usage-log.json` is a JSON array of run records. One record per run: + +```json +{ + "arm": "altair", + "run": 1, + "model": "claude-opus-4-7", + "input_tokens": 8200, + "output_tokens": 1400, + "cache_read_tokens": 0, + "turns": 6, + "tool_calls": 9, + "file_reads": 2, + "wallclock_ms": 41000, + "acceptance_pass": true +} +``` + +- `input_tokens` / `output_tokens`: summed across every model response in the run. +- `cache_read_tokens`: reported separately so caching flatters neither arm. +- `acceptance_pass`: whether the frozen suite passed on this completed attempt + (feeds pass@1). +- The arm named `altair` is treated as the reference; ratios are + `baseline / altair` (higher = bigger Altair advantage). + +## Run the scorer + +```bash +php score.php # reads results/usage-log.json +php score.php results/usage-log.sample.json # wiring test with synthetic data +``` + +It prints a per-arm table and writes `results/results.json` (medians, spread, +pass@1, and the comparison ratios) for charting. + +> **The sample log is synthetic** — illustrative numbers to prove the pipeline +> works. Never publish or chart the sample. Real results only, with the raw log +> and the `task.md` commit SHA attached. diff --git a/benchmarks/tokens-to-ship/results/usage-log.sample.json b/benchmarks/tokens-to-ship/results/usage-log.sample.json new file mode 100644 index 00000000..e22361b6 --- /dev/null +++ b/benchmarks/tokens-to-ship/results/usage-log.sample.json @@ -0,0 +1,13 @@ +[ + { "arm": "altair", "run": 1, "model": "claude-opus-4-7", "input_tokens": 8200, "output_tokens": 1400, "cache_read_tokens": 0, "turns": 6, "tool_calls": 9, "file_reads": 2, "wallclock_ms": 41000, "acceptance_pass": true }, + { "arm": "altair", "run": 2, "model": "claude-opus-4-7", "input_tokens": 7900, "output_tokens": 1250, "cache_read_tokens": 0, "turns": 5, "tool_calls": 8, "file_reads": 1, "wallclock_ms": 38500, "acceptance_pass": true }, + { "arm": "altair", "run": 3, "model": "claude-opus-4-7", "input_tokens": 9100, "output_tokens": 1600, "cache_read_tokens": 0, "turns": 7, "tool_calls": 11, "file_reads": 3, "wallclock_ms": 47000, "acceptance_pass": true }, + { "arm": "altair", "run": 4, "model": "claude-opus-4-7", "input_tokens": 8400, "output_tokens": 1350, "cache_read_tokens": 0, "turns": 6, "tool_calls": 9, "file_reads": 2, "wallclock_ms": 42500, "acceptance_pass": true }, + { "arm": "altair", "run": 5, "model": "claude-opus-4-7", "input_tokens": 8000, "output_tokens": 1300, "cache_read_tokens": 0, "turns": 6, "tool_calls": 8, "file_reads": 2, "wallclock_ms": 40000, "acceptance_pass": true }, + + { "arm": "baseline", "run": 1, "model": "claude-opus-4-7", "input_tokens": 71000, "output_tokens": 9800, "cache_read_tokens": 0, "turns": 24, "tool_calls": 48, "file_reads": 21, "wallclock_ms": 312000, "acceptance_pass": true }, + { "arm": "baseline", "run": 2, "model": "claude-opus-4-7", "input_tokens": 82000, "output_tokens": 11200, "cache_read_tokens": 0, "turns": 28, "tool_calls": 57, "file_reads": 26, "wallclock_ms": 351000, "acceptance_pass": true }, + { "arm": "baseline", "run": 3, "model": "claude-opus-4-7", "input_tokens": 68000, "output_tokens": 9100, "cache_read_tokens": 0, "turns": 22, "tool_calls": 44, "file_reads": 19, "wallclock_ms": 289000, "acceptance_pass": false }, + { "arm": "baseline", "run": 4, "model": "claude-opus-4-7", "input_tokens": 90000, "output_tokens": 12500, "cache_read_tokens": 0, "turns": 31, "tool_calls": 62, "file_reads": 29, "wallclock_ms": 378000, "acceptance_pass": true }, + { "arm": "baseline", "run": 5, "model": "claude-opus-4-7", "input_tokens": 76000, "output_tokens": 10400, "cache_read_tokens": 0, "turns": 26, "tool_calls": 52, "file_reads": 24, "wallclock_ms": 333000, "acceptance_pass": true } +] diff --git a/benchmarks/tokens-to-ship/score.php b/benchmarks/tokens-to-ship/score.php new file mode 100644 index 00000000..338be4e7 --- /dev/null +++ b/benchmarks/tokens-to-ship/score.php @@ -0,0 +1,203 @@ + $numbers + */ +function median(array $numbers): float +{ + if ($numbers === []) { + return 0.0; + } + + sort($numbers); + $count = \count($numbers); + $mid = intdiv($count, 2); + + return $count % 2 === 1 + ? (float) $numbers[$mid] + : (((float) $numbers[$mid - 1]) + ((float) $numbers[$mid])) / 2; +} + +/** + * @param array $record + */ +function totalTokens(array $record): int +{ + return (int) ($record['input_tokens'] ?? 0) + (int) ($record['output_tokens'] ?? 0); +} + +/** + * @param list> $records + * @return array>> + */ +function groupByArm(array $records): array +{ + $grouped = []; + foreach ($records as $record) { + $arm = (string) ($record['arm'] ?? 'unknown'); + $grouped[$arm][] = $record; + } + ksort($grouped); + + return $grouped; +} + +/** + * @param list> $runs + * @return array + */ +function summarizeArm(string $arm, array $runs): array +{ + $series = array_fill_keys(METRICS, []); + $passed = 0; + + foreach ($runs as $run) { + $series['total_tokens'][] = totalTokens($run); + $series['turns'][] = (int) ($run['turns'] ?? 0); + $series['tool_calls'][] = (int) ($run['tool_calls'] ?? 0); + $series['file_reads'][] = (int) ($run['file_reads'] ?? 0); + $series['wallclock_ms'][] = (int) ($run['wallclock_ms'] ?? 0); + $passed += ($run['acceptance_pass'] ?? false) === true ? 1 : 0; + } + + $summary = ['arm' => $arm, 'runs' => \count($runs)]; + foreach (METRICS as $metric) { + $summary[$metric] = [ + 'median' => median($series[$metric]), + 'min' => $series[$metric] === [] ? 0 : min($series[$metric]), + 'max' => $series[$metric] === [] ? 0 : max($series[$metric]), + ]; + } + $summary['pass_at_1'] = $runs === [] ? 0.0 : round($passed / \count($runs), 3); + + return $summary; +} + +/** + * @param array> $summaries + * @return array> + */ +function comparisons(array $summaries): array +{ + if (!isset($summaries[REFERENCE_ARM])) { + return []; + } + + $reference = $summaries[REFERENCE_ARM]; + $comparison = []; + foreach ($summaries as $arm => $summary) { + if ($arm === REFERENCE_ARM) { + continue; + } + foreach (METRICS as $metric) { + $refMedian = (float) $reference[$metric]['median']; + $comparison[$arm][$metric] = $refMedian > 0.0 + ? round(((float) $summary[$metric]['median']) / $refMedian, 2) + : 0.0; + } + } + + return $comparison; +} + +function fail(string $message): never +{ + fwrite(STDERR, $message . PHP_EOL); + exit(1); +} + +// --- Load ------------------------------------------------------------------ + +$path = $argv[1] ?? __DIR__ . '/results/usage-log.json'; +if (!is_file($path)) { + fail(\sprintf("Usage log '%s' not found. Try: php score.php results/usage-log.sample.json", $path)); +} + +$decoded = json_decode((string) file_get_contents($path), true); +if (!\is_array($decoded) || $decoded === []) { + fail(\sprintf("Usage log '%s' is empty or not a JSON array of run records.", $path)); +} + +/** @var list> $records */ +$records = array_values(array_filter($decoded, '\is_array')); + +// --- Aggregate ------------------------------------------------------------- + +$summaries = []; +foreach (groupByArm($records) as $arm => $runs) { + $summaries[$arm] = summarizeArm($arm, $runs); +} + +$results = [ + 'source' => basename($path), + 'reference_arm' => REFERENCE_ARM, + 'arms' => array_values($summaries), + 'comparison' => comparisons($summaries), + 'note' => 'comparison = baseline median / altair median (higher means a larger Altair advantage)', +]; + +$outputDir = __DIR__ . '/results'; +if (!is_dir($outputDir)) { + mkdir($outputDir, 0o755, true); +} +file_put_contents( + $outputDir . '/results.json', + json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL, +); + +// --- Report ---------------------------------------------------------------- + +printf("%-12s %6s %14s %8s %11s %13s %9s%s", 'arm', 'runs', 'tokens(med)', 'turns', 'toolcalls', 'wallclock(s)', 'pass@1', PHP_EOL); +printf("%s%s", str_repeat('-', 78), PHP_EOL); +foreach ($summaries as $summary) { + printf( + "%-12s %6d %14s %8s %11s %13s %9s%s", + $summary['arm'], + $summary['runs'], + number_format($summary['total_tokens']['median']), + number_format($summary['turns']['median']), + number_format($summary['tool_calls']['median']), + number_format($summary['wallclock_ms']['median'] / 1000, 1), + number_format($summary['pass_at_1'] * 100, 0) . '%', + PHP_EOL, + ); +} + +$comparison = $results['comparison']; +if ($comparison !== []) { + printf("%sComparison vs '%s' (x = how many times more the baseline spends):%s", PHP_EOL, REFERENCE_ARM, PHP_EOL); + foreach ($comparison as $arm => $ratios) { + printf( + " %s: %sx tokens, %sx turns, %sx wallclock%s", + $arm, + $ratios['total_tokens'], + $ratios['turns'], + $ratios['wallclock_ms'], + PHP_EOL, + ); + } +} + +printf("%sWrote %s%s", PHP_EOL, $outputDir . '/results.json', PHP_EOL); diff --git a/benchmarks/tokens-to-ship/task.md b/benchmarks/tokens-to-ship/task.md new file mode 100644 index 00000000..6f0c7ae3 --- /dev/null +++ b/benchmarks/tokens-to-ship/task.md @@ -0,0 +1,56 @@ +# Frozen task — Posts API + +> This file is the contract. It does not change between runs or arms. Any edit +> invalidates every prior result. Pin the commit SHA of this file in the report. + +## Prompt given to the agent (verbatim) + +> Add a **Posts** REST resource to the application with these endpoints: +> +> - `POST /posts` — create a post +> - `GET /posts` — list posts +> - `GET /posts/{id}` — fetch one post by id +> - `PUT /posts/{id}` — update a post +> - `DELETE /posts/{id}` — delete a post +> +> A post has: `id` (string, server-assigned), `title` (string, 1–120 chars, +> required), `body` (string, required), `published` (boolean, default false), +> `createdAt` (ISO-8601 timestamp, server-assigned). +> +> Requirements: +> 1. Input validation: `title` and `body` required on create; `title` length +> 1–120; validation failure returns HTTP 422 with per-field errors. +> 2. Persistence: posts are stored in a database (entity + migration + +> repository). Use the project's standard persistence approach. +> 3. An OpenAPI 3.1 description of all five endpoints. +> 4. A typed **TypeScript** client for the resource. +> 5. Tests covering: create happy-path, create validation failure (422), +> get-by-id found + not-found (404), and list. +> +> Stop when the acceptance suite passes. + +## Acceptance criteria (checked by the harness, not the agent) + +A run is **complete** only when an external, frozen suite passes. The suite is +identical for both arms and asserts behavior through the HTTP layer, so +implementation details are free to differ: + +- [ ] `POST /posts` with valid body → `201`, returns the created post incl. + `id` and `createdAt`. +- [ ] `POST /posts` missing `title` → `422`, body has `errors.title`. +- [ ] `POST /posts` with 121-char `title` → `422`. +- [ ] `GET /posts` → `200`, array including created posts. +- [ ] `GET /posts/{id}` for an existing id → `200`, the post. +- [ ] `GET /posts/{id}` for an unknown id → `404`. +- [ ] `PUT /posts/{id}` updates fields → `200`, reflects changes. +- [ ] `DELETE /posts/{id}` → `204`; subsequent `GET` → `404`. +- [ ] The emitted OpenAPI document is valid 3.1 and contains all five operations. +- [ ] The emitted TypeScript client **compiles** under `tsc --strict` with zero + errors. +- [ ] The project's own test suite for the feature passes. + +## Out of scope (do not build, either arm) + +Auth, pagination, sorting, soft-deletes, rate-limiting, a Python client. Keep the +task identical and minimal so the measurement is about *plumbing cost*, not +scope creep. diff --git a/docs/benchmarks/tokens-to-ship.md b/docs/benchmarks/tokens-to-ship.md new file mode 100644 index 00000000..d5cc9f4c --- /dev/null +++ b/docs/benchmarks/tokens-to-ship.md @@ -0,0 +1,143 @@ +# Benchmark methodology — "Tokens to Ship" + +> The credibility artifact behind the [positioning](../marketing/positioning.md). +> Because a software company's reputation rests on its honesty, this methodology +> is designed to be **defensible first, flattering second.** A benchmark that +> doesn't survive scrutiny is worse than no benchmark. + +--- + +## 1. The metric + +**Tokens-to-ship**: the total agent tokens (input + output) consumed to take a +fixed, realistic feature from a cold prompt to **passing an external acceptance +suite**, on each of two arms. + +We report tokens as the headline, plus four supporting metrics: + +| Metric | Why it matters | +|---|---| +| **Total tokens** (input + output) | The cost/efficiency headline. | +| **Wallclock** | Human-felt speed. | +| **Agent turns** | Round-trips with the model (latency + cost proxy). | +| **Tool calls / file reads** | Direct measure of "exploration & re-review" cost. | +| **pass@1** | Did acceptance pass on the *first* completed attempt? Guards against "fast but wrong." | + +The acceptance suite is the referee. **"Faster" only counts if the output is +correct, typed, tested, and documented to the same bar on both arms.** + +## 2. The task (frozen) + +A single realistic feature, specified once and never changed: +[`benchmarks/tokens-to-ship/task.md`](../../benchmarks/tokens-to-ship/task.md). + +> Build a **Posts** REST resource: `POST /posts`, `GET /posts`, `GET /posts/{id}`, +> `PUT /posts/{id}`, `DELETE /posts/{id}`. Input validation, persistence +> (entity + migration + repository), an OpenAPI 3.1 description, a typed +> TypeScript client, and tests for the happy path + validation failures. + +The task is deliberately a **CRUD-with-plumbing** feature — the bread-and-butter +of agency work — not a contrived codegen-friendly toy and not an +algorithmically hard problem. (See §7 for why this choice is fair and where it +isn't.) + +## 3. The two arms + +Both arms use the **same model, same agent runner, same allowed tools, same +acceptance suite, same cold-start context.** Only the framework differs. + +- **Arm A — Altair.** The agent writes the YAML spec(s), runs `spec:scaffold`, + reads the JSON receipt, runs the test reporter, and emits the SDK with + `spec:emit-sdk`. It is *permitted* to re-read generated files but not + *required* to — the receipt + verdict are designed to make re-reading + unnecessary. +- **Arm B — Baseline.** The agent hand-builds the same artifacts on a + conventional PHP setup (a minimal PSR-15 + a query-builder/ORM stack, *or* + vanilla Laravel without Boost — pick one and document it). Same acceptance + bar. + +> **Baseline honesty:** document exactly which baseline was used and its version. +> Run a Laravel-without-Boost arm *and* (optionally) a Laravel-with-Boost arm. +> If you only beat an unrealistically bare baseline, the result is worthless. +> The strongest claim is the one made against a *good* baseline. + +## 4. Fairness controls + +1. **Same model + temperature.** Record exact model ID and settings. +2. **Same system/agent prompt scaffolding**, minus framework-specific tool docs + each arm legitimately ships with (Altair's CLI/MCP docs vs. the baseline's + framework docs). This asymmetry is *the thing being measured* — but it must + be a fair, real-world setup for each, not a handicap. +3. **N repetitions** (recommend N ≥ 5 per arm). Report **median** and the + full spread (min/max), never a single lucky run. +4. **Cold start.** Each run begins with no prior conversation about the task. +5. **Identical acceptance suite**, run by the harness, not the agent. The agent + does not get to declare victory. +6. **Frozen task & frozen acceptance suite** across all runs and arms. Any change + invalidates prior runs. +7. **Pre-registered expectation.** Write down the hypothesis and the stopping + rule *before* running, so results aren't quietly cherry-picked. + +## 5. Token accounting + +Tokens are read from the agent runner's per-turn usage, summed across the +session: + +- **Boundaries.** Start = the moment the task prompt is sent. End = the moment + the acceptance suite first passes. Everything between counts; environment + setup (composer install, DB up) is **excluded** and done identically for both + arms beforehand. +- **Source of truth.** Capture `usage.input_tokens` + `usage.output_tokens` + (and cache-read/-write tokens, reported separately) from each model response. + With the Claude Agent SDK this is available per turn; with a recorded + transcript, sum the usage blocks. +- **Cache handling.** Report raw input tokens *and* cache-adjusted tokens. + Caching helps both arms; show both so neither side is flattered by it. +- **No human edits.** If a human touches the code mid-run, the run is void. + +## 6. Running it + +Two supported protocols, in increasing rigor: + +- **Scripted-agent (preferred).** Drive each arm with the **Claude Agent SDK**: + one system prompt, the task as the user turn, the arm's tools, a turn cap. + The harness captures usage per turn automatically and stops when acceptance + passes or the cap is hit. Fully reproducible. +- **Recorded-transcript (fallback).** Run the task interactively in Claude Code, + export the transcript, and feed its usage blocks to the scorer. Less hands-off + but still auditable. + +Either way, per-run records land in a usage log and are aggregated by +[`benchmarks/tokens-to-ship/score.php`](../../benchmarks/tokens-to-ship/score.php) +into `results/results.json` plus a printed table. + +## 7. Threats to validity (state these publicly) + +Publishing the weaknesses is what makes the strength believable. + +- **Task selection bias.** CRUD-with-plumbing structurally favors deterministic + codegen. That is a *real* advantage for this class of work — which is most + agency work — but do **not** generalize it to "Altair is N× faster at + everything." Scope the claim to the task class. Consider a second, + non-CRUD task to show where the gap shrinks; reporting that *increases* trust. +- **Baseline strength.** A weak baseline inflates the result. Use a credible, + current baseline and say which. +- **Model variance.** Hence N runs + median + spread. +- **Maturity asymmetry.** Altair is new and narrow; the baseline is mature and + general. Note it. +- **Author effect.** The people who built Altair run the benchmark. Mitigate by + publishing the full harness, task, acceptance suite, and raw logs so anyone + can rerun it. Reproducibility is the answer to "you're biased." + +## 8. Reporting + +- **Headline:** median total tokens, Altair vs. baseline, with the ratio. +- **Chart:** grouped bars (tokens, turns, wallclock), Altair vs. baseline(s), + error bars = spread. +- **Table:** every metric, every arm, N, median, min, max, pass@1. +- **Receipts:** link the raw usage logs and the exact task + acceptance commit + SHA. +- **Caveats:** §7, in plain language, on the same page as the chart. + +The goal is a number a skeptical senior engineer reads and thinks *"that's +honest, and it's still impressive."* That reaction is the entire marketing win. diff --git a/docs/marketing/positioning.md b/docs/marketing/positioning.md new file mode 100644 index 00000000..0670138f --- /dev/null +++ b/docs/marketing/positioning.md @@ -0,0 +1,135 @@ +# Positioning — Altair, by 2am.tech + +> Working positioning for the flagship. The framework's marketing name (`Altair`) +> is a placeholder you can swap; the namespace is `Altair\*`, the package is +> `univeros/framework`. Everything below is the narrative, not the API. + +--- + +## 1. One line + +**Altair is the framework where agents ship software, not shop for it.** + +You describe intent; the framework deterministically produces the typed, +tested, documented implementation — so an AI agent spends its tokens on *what +to build*, never on boilerplate, exploration, or re-reading its own work. + +## 2. What this actually is (and the 2am.tech angle) + +Altair is **2am.tech's open proof-of-work**. It is not a product we sell and +not a Laravel competitor. It is the framework we built to answer one question +in public: *how fast and how cheaply can an AI agent deliver production +software when the framework is designed for the agent from the core out?* + +The pitch to a prospective client is not "use our framework." It is: **"this is +how we think about AI-assisted delivery — here it is, running, measured, and +reproducible."** The framework is the evidence; the engagement is the product. + +## 3. The wedge + +Laravel won the last cycle against a heavier Symfony on a few novel +developer-experience ideas. Laravel is now the heavy incumbent: too large to +make core architectural changes, so it adds **layers beside the framework** — +Laravel Boost is an MCP server and a guidelines/skills/docs layer *next to* +Laravel, not inside it. + +That is the opening. An incumbent can bolt agent tooling onto the side. It +cannot retrofit agent-native decisions into the core without breaking everyone. + +**Altair's bet: agent-native at the core, not agent-tooling on the side.** + +- Boost makes an agent a *better freehand writer* of Laravel code. +- Altair makes the agent *stop writing most of the code at all* — it compiles + intent to artifacts, returns a machine receipt, and makes mistakes one command + to undo. + +Different philosophy, not a feature race: **deterministic regeneration vs. +better autocomplete.** + +## 4. The thesis to brand around + +> **The deterministic part is the framework's job. The intent is yours.** + +Every token an agent burns today on plumbing, discovery, or re-verification is a +token a framework *should* eliminate by design. Altair is organized entirely +around removing those token sinks. + +| Token / time sink today | The layer that kills it | Agent payoff | +|---|---|---| +| Writing & reading boilerplate | **Intent compiler** — one spec emits Action/Input/Responder/entity/migration/test/OpenAPI/SDK | 1 spec edit, not N file writes | +| Re-reviewing its own output | **Deterministic output + JSON receipt** (files, SHAs, drift, test verdict) | Reads a 5-line receipt, never re-reads generated code | +| Parsing human-prose errors | **Machine-first surfaces** — one canonical verdict (`result: pass\|fail\|drift`) | Branches on one line, not a stack trace | +| Defensive "check before I act" loops | **Reversibility** — journaled, rewindable mutations | Act-then-verify; a mistake costs one `rewind` | +| Re-deriving project state each session | **Load-once self-map** — manifest/codemap + typed MCP tools + event log | Whole project shape in a few hundred tokens | + +## 5. The "watch this" demo (90 seconds) + +The flagship lives or dies on one demonstration. The story: + +1. An agent is handed: *"Add a Posts API: create, list, get-by-id, update, + delete, with validation and persistence, plus a typed TypeScript client."* +2. The agent writes a short YAML spec (intent only — ~20 lines). +3. `spec:scaffold` emits the full implementation; the agent reads a compact JSON + receipt, not the generated files. +4. `spec:emit-sdk typescript` emits the typed client. +5. The test reporter returns `{"result":"pass"}`; the agent stops. +6. Split-screen counter: **tokens spent vs. the same task on a conventional + stack.** The gap is the whole pitch. + +The demo's emotional beat is the moment the agent *doesn't* re-read its work — +because it doesn't need to. + +## 6. Proof, not claims + +A software company's credibility is its product, so the marketing rests on a +**reproducible benchmark**, not adjectives. See +[`docs/benchmarks/tokens-to-ship.md`](../benchmarks/tokens-to-ship.md): the same +defined feature, built by the same agent/model, on Altair vs. a conventional +baseline, measured in **agent tokens and wallclock**, gated by an external +acceptance suite so "fast" can never mean "wrong." + +The headline asset is one honest chart: *tokens-to-ship, Altair vs. baseline.* + +## 7. Honest scope (say this out loud) + +- **Not** competing for framework market share. No "Laravel killer" language — + ever. It reads as insecure and invites the wrong comparison. +- **Not** the app-building AI layer (LLM calls, RAG). That space is taken + (`laravel/ai`, Prism, NeuronAI, LLPhant). Altair is about *building software + with agents*, not *building AI into apps*. +- **Is** a focused demonstration of a specific, defensible idea: a + deterministic, spec-driven, fully reversible framework where agents are + first-class by architecture — and the runnable evidence that 2am.tech operates + at that frontier. + +## 8. Messaging kit + +**Candidate taglines** +- "Spec in. Software out. Minimal tokens between." +- "Agents ship, not shop." +- "The deterministic part is the framework's job." +- "Build production APIs at agent speed." +- "Agent-native at the core — not bolted on the side." + +**Words to avoid:** "Laravel killer," "replaces," "the only," "the first." +Lead with what it *does*, backed by the number. + +**Tone:** confident, measured, evidence-first. The benchmark does the bragging. + +**Primary CTA:** not "install" — **"watch the 90-second build"** and **"read the +methodology."** The conversion goal is a conversation with 2am.tech, not a +`composer require`. + +## 9. Distribution (where this gets seen) + +- A single landing page: hero one-liner, the 90-second video, the benchmark + chart, a "how we build" link to 2am.tech services. +- One launch artifact: a written deep-dive ("We made an AI agent ship a + production API in N tokens — here's the framework and the method"). Aim it at + HN / Laravel News / r/PHP / dev.to. The method + honesty is the hook. +- Conference talk / lightning demo: the split-screen token counter is a great + live moment. + +The bias to resist: shipping breadth (19 roadmap features) reads as a changelog. +**One flawless loop + one honest benchmark beats a long feature list** for a +marketing flagship. diff --git a/docs/strategy/ai-driven-strategy.md b/docs/strategy/ai-driven-strategy.md new file mode 100644 index 00000000..084ade7b --- /dev/null +++ b/docs/strategy/ai-driven-strategy.md @@ -0,0 +1,175 @@ +# AI-Driven Strategy Spec — Altair, by 2am.tech + +> Companion to [positioning](../marketing/positioning.md) and the +> [tokens-to-ship benchmark](../benchmarks/tokens-to-ship.md). This is the +> *strategy*, not an implementation plan — decisions here gate what gets built. +> Living document; revise as reality teaches us. + +--- + +## 0. The reframe that drives everything + +Two kinds of "lazy" point at two different markets: + +- **Yii laziness (developers):** "One command and voilà — done." Wants maximum + output per unit of effort. Will `composer require`. +- **The bigger laziness (everyone else):** *won't even learn a framework.* They + don't want Laravel, or PHP, or "a framework" at all. **They want an app that + works and does X, Y, Z.** They will never open a terminal — they'll talk to an + agent. + +The second group is larger, growing faster, and far less served. AI is what +makes serving it possible. **We design for that group as the destination, and +earn credibility + revenue through developers and 2am.tech engagements on the +way there.** + +## 1. The insight: AI changes *who the user is* + +Before AI, a framework's user is a developer, so frameworks compete on +developer experience ("easier to learn than the other one"). + +After AI, for the outcome-seeker, **the framework's operator is an agent** and +the human only states intent. That inverts the design goals: + +- "Easy for a human to learn" becomes irrelevant. +- "Reliable for an agent to operate" becomes everything. +- The human surface is *natural language → intent*; the agent turns intent into + specs; the framework deterministically turns specs into working software. + +This is why determinism, reversibility, and machine-first surfaces are not +nice-to-haves. They are the **enabling conditions** for trustworthy +agent-generated apps. A framework an agent can operate without breaking things +beats a framework a human finds pleasant. + +## 2. Landscape (honest) + +| Player | Serves | What they do | The gap they leave | +|---|---|---|---| +| **Laravel** (+ Cashier, Boost, laravel/ai) | Developers | Mature framework; agent tooling **bolted on the side** (Boost MCP) | Not aimed at "no framework knowledge → working app"; agent-nativeness is external | +| **AI app builders** (v0, Lovable, Bolt, Replit Agent) | Outcome-seekers | Generate full-stack JS fast | Output is often **fragile, throwaway, weak on real backend** (payments, data integrity, webhooks, migrations); optimized for the demo, not the maintainable app | +| **No-code** (Bubble, Retool) | Outcome-seekers | Visual app building | Lock-in, ceilings, you don't own real code | + +**The unclaimed seam:** *a reliable backend/app substrate an agent operates to +produce real, owned, maintainable apps — with the hard backend features done +correctly.* Nobody owns "AI-built apps that actually keep working, with code you +own." That is the wedge, and our deterministic/reversible core is exactly what +the JS app-builders cannot easily retrofit. + +## 3. Product thesis + +> **Describe the app you want. An agent builds it on a substrate engineered so +> the result actually works, keeps working, and is yours to own.** + +Three pillars: + +1. **Intent → spec → working software, deterministically.** The Yii "voilà," + now spoken in plain English. +2. **Reliability by construction.** Features come from proven, tested, + reversible spec modules — not freehand generation. Mistakes cost one + `rewind`. +3. **You own the code.** Real PHP, real database, exportable, no lock-in. This + is the answer to both no-code (lock-in) and JS builders (throwaway). + +## 4. The "X, Y, Z features" product: a capability catalog + +The user's own phrasing — *"an app that works with xyz features"* — **is** the +product. Operationalize it as a **catalog of composable, agent-installable +capability modules.** Each module is a deterministic spec module that emits +entity + migration + endpoints + tests + OpenAPI + typed client, **with the +painful edge-cases already handled**: + +- auth (sessions, tokens, social login) +- payments / billing (Stripe-first; idempotent webhooks; reconciliation) +- notifications (email / SMS / push; retries; outbox) +- file uploads (storage, validation, scan hook) +- search, CRUD resources, admin surface, audit log, rate limiting, multi-tenancy + +> *"I want an app for X with payments and email notifications"* → the agent +> selects catalog modules → composes them → working, owned app. + +Each module is **simultaneously** a cheap stand-alone tool for developers (Tier +A) **and** a building block for the "describe → app" experience (Tier B). The +catalog is the moat: a growing library of *correct, reversible, +agent-installable* capabilities is something neither Laravel (human-first +packages) nor the JS builders (freehand generation) have. + +This reframes the roadmap: **stop building "framework features"; start building +"capability modules" with demonstrable outcomes.** + +## 5. Gap map → sequenced shortlist + +| # | Module | Gap it fills | Tier | Adoption cost | Dev + agent payoff | Demo hook | +|---|---|---|---|---|---|---| +| 1 | **Webhook + idempotency + outbox** | Universal "react to an untrusted callback exactly once" — unowned, hand-rolled, buggy | A + B | `composer require` | Exactly-once reliability for any provider | "GitHub/Stripe webhook handled correctly from one spec" | +| 2 | **Intent compiler (Gii-for-AI)**, framework-agnostic | Boilerplate elimination; the Yii lever | A → B substrate | CLI, no lock-in | One spec → CRUD + persistence + OpenAPI + SDK + tests | the 90-second build | +| 3 | **Stripe payments kit** (builds on #1) | Correct billing plumbing nobody owns generically | A + B | module | Idempotent webhooks, state machine, reconciliation, sandbox | "subscriptions that don't double-charge" | +| 4 | **MCP "ship a feature" server** (generative + reversible) | Agent operability in *any* project | B | point your agent at it | Agent scaffolds / migrates / rewinds anywhere | agent live-builds an endpoint on camera | +| 5 | **Capability catalog + composer** ("describe app → working app") | The Tier-B destination | B | hosted / agent-driven | Natural-language app assembly from proven modules | the flagship demo | + +**Sequence:** #1 → #2 → (#3 ∥ #4) → #5. Each ships real value and a marketing +moment on its own; #5 is the synthesis everything builds toward. + +**First move:** #1 (webhook+idempotency+outbox). It's cheap to adopt, broadly +useful beyond payments, demonstrable in 60 seconds, fills a *real* gap, and is +the foundation #3 sits on. #2 (the intent compiler) is the close second and the +purest showcase. + +## 6. How each step feeds 2am.tech marketing + +- Every module is a launchable artifact (Laravel News / HN / r/PHP / dev.to) and + a lead-gen demo — adoption *is* the marketing. +- The [tokens-to-ship benchmark](../benchmarks/tokens-to-ship.md) is the + credibility spine under all of it. +- "Describe app → working app" is the showpiece that says *"2am.tech builds at + the frontier"* louder than any written case study. + +## 7. Discipline — what NOT to do + +- **Don't** make a learn-it framework the adoption unit (that's the switching + cost that protects Laravel; we route around it). +- **Don't** start with a multi-provider payment abstraction — Stripe first, with + escape hatches. +- **Don't** chase Laravel feature-parity — chase the property-gaps + (agent-operability, determinism, reversibility) and the unowned edge-cases. +- **Don't** fall into the v0/Lovable trap of demo-only output. Maintainability + + ownership is our differentiator; if a generated app isn't genuinely + maintainable, we've lost our only edge. +- **Don't** ship breadth as a changelog; ship depth as demos. + +## 8. Risks / threats to validity (name them) + +- **Tier B is hard and crowded.** Our bet is *backend reliability + ownership*, + which must be **visibly, demonstrably true** — not asserted. The reversibility + and maintainability have to show up in the demo. +- **PHP perception.** Tier-B founders assume JS/Python. Counter: they don't care + about the language if the app works and they own it. Lead with outcomes, never + with "PHP." +- **Catalog quality is the moat — and the liability.** One fragile module + poisons trust. Every module ships behind an acceptance suite (same philosophy + as the benchmark). Quality gate is non-negotiable. +- **Capacity (solo + agent).** Sequence ruthlessly; resist parallel half-builds. + #1 and #2 before anything else. + +## 9. Open questions to resolve before building #5 + +1. **Hosted vs. self-hosted** for the "describe → app" experience? Hosted = + monetization + control of the experience; self-hosted = ownership purity. + (Likely: hosted experience that *exports* an owned, self-hostable codebase — + best of both.) +2. **Which five catalog modules ship first?** Decide by "most-requested real app + features," not by what's easiest to build. +3. **Brand** for the Tier-B experience — probably distinct from "Altair the + framework," since Tier B never needs to know the framework's name. + +--- + +### TL;DR + +Two markets: developers who want less effort, and a bigger group who won't learn +a framework at all and just want a working app. AI lets us serve the second by +making the framework's *operator* an agent. The unclaimed seam is **reliable, +owned, maintainable AI-built apps** — which our deterministic, reversible, +spec-driven core is uniquely suited to. Build it as a **catalog of +agent-installable capability modules**; each module is a cheap developer tool +*and* a building block for "describe → app." Start with webhook+idempotency, then +the intent compiler. Honest benchmarks and live demos are the marketing. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fd82d9ef..714fcd3e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -56,20 +56,65 @@ parameters: path: src/Altair/Common/Support/Arr.php - - message: "#^Method Altair\\\\Container\\\\Collection\\\\AliasesCollection\\:\\:define\\(\\) should return Altair\\\\Container\\\\Collection\\\\AliasesCollection but returns Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" + message: "#^Class Altair\\\\Configuration\\\\Collection\\\\ConfigurationCollection extends generic class Altair\\\\Structure\\\\Set but does not specify its types\\: TValue$#" + count: 1 + path: src/Altair/Configuration/Collection/ConfigurationCollection.php + + - + message: "#^Class Altair\\\\Container\\\\Collection\\\\AliasesCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/AliasesCollection.php + + - + message: "#^Method Altair\\\\Container\\\\Collection\\\\AliasesCollection\\:\\:define\\(\\) should return Altair\\\\Container\\\\Collection\\\\AliasesCollection but returns Altair\\\\Structure\\\\Contracts\\\\MapInterface\\\\.$#" count: 1 path: src/Altair/Container/Collection/AliasesCollection.php + - + message: "#^Class Altair\\\\Container\\\\Collection\\\\ClassDefinitionsCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/ClassDefinitionsCollection.php + + - + message: "#^Class Altair\\\\Container\\\\Collection\\\\DelegatesCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/DelegatesCollection.php + + - + message: "#^Class Altair\\\\Container\\\\Collection\\\\ParameterDefinitionsCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/ParameterDefinitionsCollection.php + + - + message: "#^Class Altair\\\\Container\\\\Collection\\\\PreparesCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/PreparesCollection.php + + - + message: "#^Class Altair\\\\Container\\\\Collection\\\\SharesCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/SharesCollection.php + - message: "#^Method Altair\\\\Container\\\\Collection\\\\SharesCollection\\:\\:shareClass\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" count: 1 path: src/Altair/Container/Collection/SharesCollection.php + - + message: "#^Method Altair\\\\Container\\\\Collection\\\\SharesCollection\\:\\:shareClass\\(\\) return type with generic interface Altair\\\\Structure\\\\Contracts\\\\MapInterface does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/SharesCollection.php + - message: "#^Method Altair\\\\Container\\\\Collection\\\\SharesCollection\\:\\:shareInstance\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" count: 1 path: src/Altair/Container/Collection/SharesCollection.php + - + message: "#^Method Altair\\\\Container\\\\Collection\\\\SharesCollection\\:\\:shareInstance\\(\\) return type with generic interface Altair\\\\Structure\\\\Contracts\\\\MapInterface does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Container/Collection/SharesCollection.php + - message: "#^Call to an undefined method ReflectionFunctionAbstract\\:\\:invokeArgs\\(\\)\\.$#" count: 1 @@ -86,7 +131,37 @@ parameters: path: src/Altair/Container/Executable.php - - message: "#^Access to an undefined property Altair\\\\Structure\\\\Contracts\\\\PairInterface\\:\\:\\$value\\.$#" + message: "#^Class Altair\\\\Cookie\\\\Collection\\\\CookieCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/CookieCollection.php + + - + message: "#^Generic type Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\ in PHPDoc tag @return specifies 2 template types, but interface Altair\\\\Structure\\\\Contracts\\\\VectorInterface supports only 1\\: TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/CookieCollection.php + + - + message: "#^Method Altair\\\\Cookie\\\\Collection\\\\CookieCollection\\:\\:pairsToArray\\(\\) has parameter \\$pairs with generic class Altair\\\\Structure\\\\Pair but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/CookieCollection.php + + - + message: "#^Method Altair\\\\Cookie\\\\Collection\\\\CookieCollection\\:\\:values\\(\\) should return Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\ but returns Altair\\\\Structure\\\\Vector\\\\.$#" + count: 1 + path: src/Altair/Cookie/Collection/CookieCollection.php + + - + message: "#^PHPDoc tag @var for variable \\$pair contains generic class Altair\\\\Structure\\\\Pair but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/CookieCollection.php + + - + message: "#^Return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Cookie\\\\Collection\\\\CookieCollection\\:\\:values\\(\\) should be compatible with return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\\\:\\:values\\(\\)$#" + count: 1 + path: src/Altair/Cookie/Collection/CookieCollection.php + + - + message: "#^Return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Cookie\\\\Collection\\\\CookieCollection\\:\\:values\\(\\) should be compatible with return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Structure\\\\Map\\\\:\\:values\\(\\)$#" count: 1 path: src/Altair/Cookie/Collection/CookieCollection.php @@ -95,6 +170,41 @@ parameters: count: 1 path: src/Altair/Cookie/Collection/CookieCollection.php + - + message: "#^Class Altair\\\\Cookie\\\\Collection\\\\SetCookieCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + + - + message: "#^Generic type Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\ in PHPDoc tag @return specifies 2 template types, but interface Altair\\\\Structure\\\\Contracts\\\\VectorInterface supports only 1\\: TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + + - + message: "#^Method Altair\\\\Cookie\\\\Collection\\\\SetCookieCollection\\:\\:pairsToArray\\(\\) has parameter \\$pairs with generic class Altair\\\\Structure\\\\Pair but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + + - + message: "#^Method Altair\\\\Cookie\\\\Collection\\\\SetCookieCollection\\:\\:values\\(\\) should return Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\ but returns Altair\\\\Structure\\\\Vector\\\\.$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + + - + message: "#^PHPDoc tag @var for variable \\$pair contains generic class Altair\\\\Structure\\\\Pair but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + + - + message: "#^Return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Cookie\\\\Collection\\\\SetCookieCollection\\:\\:values\\(\\) should be compatible with return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\\\:\\:values\\(\\)$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + + - + message: "#^Return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Cookie\\\\Collection\\\\SetCookieCollection\\:\\:values\\(\\) should be compatible with return type \\(Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\\\) of method Altair\\\\Structure\\\\Map\\\\:\\:values\\(\\)$#" + count: 1 + path: src/Altair/Cookie/Collection/SetCookieCollection.php + - message: "#^Unsafe usage of new static\\(\\)\\.$#" count: 1 @@ -135,6 +245,11 @@ parameters: count: 1 path: src/Altair/Courier/Strategy/CommandRunnerMiddlewareStrategy.php + - + message: "#^Class Altair\\\\Courier\\\\Support\\\\MessageCommandMap extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Courier/Support/MessageCommandMap.php + - message: "#^PHPDoc tag @var for property Altair\\\\Http\\\\Base\\\\Payload\\:\\:\\$settingsCollection with type Altair\\\\Structure\\\\Map\\|null is not subtype of native type Altair\\\\Http\\\\Collection\\\\SettingsCollection\\.$#" count: 1 @@ -175,6 +290,26 @@ parameters: count: 1 path: src/Altair/Http/Collection/HttpStatusCollection.php + - + message: "#^Class Altair\\\\Http\\\\Collection\\\\InputCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Http/Collection/InputCollection.php + + - + message: "#^Class Altair\\\\Http\\\\Collection\\\\MiddlewareCollection extends generic class Altair\\\\Structure\\\\Queue but does not specify its types\\: TValue$#" + count: 1 + path: src/Altair/Http/Collection/MiddlewareCollection.php + + - + message: "#^Class Altair\\\\Http\\\\Collection\\\\RouteCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Http/Collection/RouteCollection.php + + - + message: "#^Class Altair\\\\Http\\\\Collection\\\\SettingsCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/Altair/Http/Collection/SettingsCollection.php + - message: "#^Class Lcobucci\\\\Jose\\\\Parsing\\\\Decoder not found\\.$#" count: 1 @@ -285,6 +420,11 @@ parameters: count: 1 path: src/Altair/Messaging/Transport/TransportRegistry.php + - + message: "#^Method Altair\\\\Middleware\\\\Runner\\:\\:__construct\\(\\) has parameter \\$queue with generic class Altair\\\\Structure\\\\Queue but does not specify its types\\: TValue$#" + count: 1 + path: src/Altair/Middleware/Runner.php + - message: "#^Property Altair\\\\Persistence\\\\Cycle\\\\CycleRepository\\:\\:\\$repository with generic interface Cycle\\\\ORM\\\\RepositoryInterface does not specify its types\\: TEntity$#" count: 1 @@ -296,19 +436,19 @@ parameters: path: src/Altair/Persistence/Cycle/CycleRepository.php - - message: "#^Method Altair\\\\Sanitation\\\\Collection\\\\FilterCollection\\:\\:put\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" + message: "#^Class Altair\\\\Sanitation\\\\Collection\\\\FilterCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" count: 1 path: src/Altair/Sanitation/Collection/FilterCollection.php - - message: "#^Method Altair\\\\Sanitation\\\\Collection\\\\FilterCollection\\:\\:putAll\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" + message: "#^PHPDoc tag @return with type mixed is not subtype of native type int\\|null\\.$#" count: 1 - path: src/Altair/Sanitation/Collection/FilterCollection.php + path: src/Altair/Sanitation/Filter/IntegerFilter.php - - message: "#^PHPDoc tag @return with type mixed is not subtype of native type int\\|null\\.$#" + message: "#^Method Altair\\\\Sanitation\\\\FiltersRunner\\:\\:__construct\\(\\) has parameter \\$queue with generic class Altair\\\\Structure\\\\Queue but does not specify its types\\: TValue$#" count: 1 - path: src/Altair/Sanitation/Filter/IntegerFilter.php + path: src/Altair/Sanitation/FiltersRunner.php - message: "#^PHPDoc tag @param for parameter \\$resolver with type callable is not subtype of native type Altair\\\\Sanitation\\\\Contracts\\\\ResolverInterface\\|null\\.$#" @@ -341,1279 +481,69 @@ parameters: path: src/Altair/Session/Handler/PdoSessionHandler.php - - message: "#^Interface Altair\\\\Structure\\\\Contracts\\\\CollectionInterface extends generic interface Traversable but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Contracts/CollectionInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\CollectionInterface\\:\\:clear\\(\\) return type has no value type specified in iterable type static\\(Altair\\\\Structure\\\\Contracts\\\\CollectionInterface\\)\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/CollectionInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\CollectionInterface\\:\\:copy\\(\\) return type has no value type specified in iterable type static\\(Altair\\\\Structure\\\\Contracts\\\\CollectionInterface\\)\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/CollectionInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\CollectionInterface\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/CollectionInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\HashableInterface\\:\\:equals\\(\\) has parameter \\$obj with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/HashableInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:apply\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:diff\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:diff\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:intersect\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:intersect\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:keys\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:ksort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:map\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:pairs\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:put\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:putAll\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:putAll\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:putAll\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:putAll\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:union\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:union\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:values\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:xor\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\MapInterface\\:\\:xor\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/MapInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\PairInterface\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/PairInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\QueueInterface\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\QueueInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/QueueInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:apply\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:insert\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:map\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 10 + path: src/Altair/Structure/Map.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + message: "#^Unsafe usage of new static\\(\\)\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Structure/Pair.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" + message: "#^Property Altair\\\\Structure\\\\PriorityNode\\\\:\\:\\$priority \\(int\\) does not accept null\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Structure/PriorityNode.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" + message: "#^Property Altair\\\\Structure\\\\PriorityNode\\\\:\\:\\$stamp \\(int\\) does not accept null\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Structure/PriorityNode.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" + message: "#^Unsafe usage of new static\\(\\)\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Structure/PriorityQueue.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 2 + path: src/Altair/Structure/Queue.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:rotate\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 7 + path: src/Altair/Structure/Set.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:set\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 2 + path: src/Altair/Structure/Stack.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" + message: "#^Class Altair\\\\Validation\\\\Collection\\\\RuleCollection extends generic class Altair\\\\Structure\\\\Map but does not specify its types\\: TKey, TValue$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Validation/Collection/RuleCollection.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" + message: "#^Binary operation \"\\*\\=\" between non\\-empty\\-string and 2 results in an error\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Validation/Rule/CreditCardRule.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\:\\:unshift\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" + message: "#^Binary operation \"\\-\" between 32 and string results in an error\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SequenceInterface.php + path: src/Altair/Validation/Rule/IpRule.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:add\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" + message: "#^Strict comparison using \\=\\=\\= between 1 and 0 will always evaluate to false\\.$#" count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php + path: src/Altair/Validation/Rule/IsbnRule.php - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:allocate\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" + message: "#^Method Altair\\\\Validation\\\\RulesRunner\\:\\:__construct\\(\\) has parameter \\$queue with generic class Altair\\\\Structure\\\\Queue but does not specify its types\\: TValue$#" count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:diff\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:diff\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:getMap\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:intersect\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:intersect\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:remove\\(\\) has no return type specified\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:union\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:union\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:xor\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\SetInterface\\:\\:xor\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/SetInterface.php - - - - message: "#^Method Altair\\\\Structure\\\\Contracts\\\\StackInterface\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\StackInterface\\.$#" - count: 1 - path: src/Altair/Structure/Contracts/StackInterface.php - - - - message: "#^Class Altair\\\\Structure\\\\Deque implements generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Class Altair\\\\Structure\\\\Deque implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:apply\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:contains\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:find\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:insert\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:insert\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:map\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:push\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:rotate\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:set\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:set\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:unshift\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Method Altair\\\\Structure\\\\Deque\\:\\:unshift\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Property Altair\\\\Structure\\\\Deque\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Deque.php - - - - message: "#^Access to an undefined property Altair\\\\Structure\\\\Contracts\\\\PairInterface\\:\\:\\$value\\.$#" - count: 4 - path: src/Altair/Structure/Map.php - - - - message: "#^Class Altair\\\\Structure\\\\Map implements generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Class Altair\\\\Structure\\\\Map implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:apply\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:diff\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:diff\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:get\\(\\) has parameter \\$default with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:get\\(\\) has parameter \\$key with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:hasKey\\(\\) has parameter \\$key with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:hasValue\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:intersect\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:intersect\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:keys\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:ksort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:map\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:pairs\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:pairsToArray\\(\\) has parameter \\$pairs with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:pairsToArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:put\\(\\) has parameter \\$key with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:put\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:put\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:putAll\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:putAll\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:putAll\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:putAll\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:remove\\(\\) has parameter \\$default with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:remove\\(\\) has parameter \\$key with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:union\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:union\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:values\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\VectorInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:xor\\(\\) has parameter \\$map with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Map\\:\\:xor\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Property Altair\\\\Structure\\\\Map\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Map.php - - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 10 - path: src/Altair/Structure/Map.php - - - - message: "#^Method Altair\\\\Structure\\\\Pair\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Pair.php - - - - message: "#^Method Altair\\\\Structure\\\\Pair\\:\\:equalsKey\\(\\) has parameter \\$key with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Pair.php - - - - message: "#^Method Altair\\\\Structure\\\\Pair\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Pair.php - - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 1 - path: src/Altair/Structure/Pair.php - - - - message: "#^Property Altair\\\\Structure\\\\PriorityNode\\:\\:\\$priority \\(int\\) does not accept null\\.$#" - count: 1 - path: src/Altair/Structure/PriorityNode.php - - - - message: "#^Property Altair\\\\Structure\\\\PriorityNode\\:\\:\\$stamp \\(int\\) does not accept null\\.$#" - count: 1 - path: src/Altair/Structure/PriorityNode.php - - - - message: "#^Access to an undefined property Altair\\\\Structure\\\\Contracts\\\\PriorityNodeInterface\\:\\:\\$priority\\.$#" - count: 2 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Access to an undefined property Altair\\\\Structure\\\\Contracts\\\\PriorityNodeInterface\\:\\:\\$stamp\\.$#" - count: 2 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Access to an undefined property Altair\\\\Structure\\\\Contracts\\\\PriorityNodeInterface\\:\\:\\$value\\.$#" - count: 3 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Class Altair\\\\Structure\\\\PriorityQueue implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Method Altair\\\\Structure\\\\PriorityQueue\\:\\:__construct\\(\\) has parameter \\$heap with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Method Altair\\\\Structure\\\\PriorityQueue\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Method Altair\\\\Structure\\\\PriorityQueue\\:\\:copy\\(\\) should return static\\(Altair\\\\Structure\\\\PriorityQueue\\) but returns Altair\\\\Structure\\\\PriorityQueue\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Method Altair\\\\Structure\\\\PriorityQueue\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Method Altair\\\\Structure\\\\PriorityQueue\\:\\:peek\\(\\) has no return type specified\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Method Altair\\\\Structure\\\\PriorityQueue\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Property Altair\\\\Structure\\\\PriorityQueue\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Property Altair\\\\Structure\\\\PriorityQueue\\:\\:\\$stamp has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 1 - path: src/Altair/Structure/PriorityQueue.php - - - - message: "#^Call to an undefined method Altair\\\\Structure\\\\Queue\\:\\:adjustCapacity\\(\\)\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Class Altair\\\\Structure\\\\Queue implements generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Class Altair\\\\Structure\\\\Queue implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:push\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\QueueInterface\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Method Altair\\\\Structure\\\\Queue\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Property Altair\\\\Structure\\\\Queue\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Queue.php - - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 2 - path: src/Altair/Structure/Queue.php - - - - message: "#^Call to an undefined method Altair\\\\Structure\\\\Set\\:\\:adjustCapacity\\(\\)\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Class Altair\\\\Structure\\\\Set implements generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Class Altair\\\\Structure\\\\Set implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:add\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:add\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:contains\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:diff\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:diff\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:getMap\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:intersect\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:intersect\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:remove\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:union\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:union\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:xor\\(\\) has parameter \\$set with no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Method Altair\\\\Structure\\\\Set\\:\\:xor\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SetInterface\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Property Altair\\\\Structure\\\\Set\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Set.php - - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 7 - path: src/Altair/Structure/Set.php - - - - message: "#^Call to an undefined method Altair\\\\Structure\\\\Stack\\:\\:adjustCapacity\\(\\)\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Class Altair\\\\Structure\\\\Stack implements generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Class Altair\\\\Structure\\\\Stack implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:push\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\StackInterface\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Method Altair\\\\Structure\\\\Stack\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Property Altair\\\\Structure\\\\Stack\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Stack.php - - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 2 - path: src/Altair/Structure/Stack.php - - - - message: "#^Class Altair\\\\Structure\\\\Vector implements generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Class Altair\\\\Structure\\\\Vector implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:__debugInfo\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:apply\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:contains\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:filter\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:find\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:insert\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:insert\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:map\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:merge\\(\\) has parameter \\$values with no value type specified in iterable type array\\|Traversable\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:merge\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:normalizeItems\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:push\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:push\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:reverse\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:rotate\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:set\\(\\) has parameter \\$value with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:set\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:slice\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:sort\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:toArray\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:unshift\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Structure\\\\Vector\\:\\:unshift\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\SequenceInterface\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Property Altair\\\\Structure\\\\Vector\\:\\:\\$internal has no type specified\\.$#" - count: 1 - path: src/Altair/Structure/Vector.php - - - - message: "#^Method Altair\\\\Validation\\\\Collection\\\\RuleCollection\\:\\:put\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Validation/Collection/RuleCollection.php - - - - message: "#^Method Altair\\\\Validation\\\\Collection\\\\RuleCollection\\:\\:putAll\\(\\) return type has no value type specified in iterable type Altair\\\\Structure\\\\Contracts\\\\MapInterface\\.$#" - count: 1 - path: src/Altair/Validation/Collection/RuleCollection.php - - - - message: "#^Binary operation \"\\*\\=\" between non\\-empty\\-string and 2 results in an error\\.$#" - count: 1 - path: src/Altair/Validation/Rule/CreditCardRule.php - - - - message: "#^Binary operation \"\\-\" between 32 and string results in an error\\.$#" - count: 1 - path: src/Altair/Validation/Rule/IpRule.php - - - - message: "#^Strict comparison using \\=\\=\\= between 1 and 0 will always evaluate to false\\.$#" - count: 1 - path: src/Altair/Validation/Rule/IsbnRule.php + path: src/Altair/Validation/RulesRunner.php - message: "#^PHPDoc tag @param for parameter \\$resolver with type callable is not subtype of native type Altair\\\\Validation\\\\Contracts\\\\ResolverInterface\\|null\\.$#" diff --git a/src/Altair/Structure/Contracts/CollectionInterface.php b/src/Altair/Structure/Contracts/CollectionInterface.php index 2c582691..b7ffb38c 100644 --- a/src/Altair/Structure/Contracts/CollectionInterface.php +++ b/src/Altair/Structure/Contracts/CollectionInterface.php @@ -15,6 +15,12 @@ use JsonSerializable; use Traversable; +/** + * @template TKey + * @template TValue + * + * @extends Traversable + */ interface CollectionInterface extends Traversable, Countable, JsonSerializable { /** @@ -50,6 +56,11 @@ public function isEmpty(): bool; * The format of the returned array is implementation-dependent. * Some implementations may throw an exception if an array representation * could not be created. + * + * Keys are narrowed to array-key because PHP arrays cannot hold arbitrary + * key types even when the collection's TKey is unconstrained. + * + * @return array */ public function toArray(): array; diff --git a/src/Altair/Structure/Contracts/HashableInterface.php b/src/Altair/Structure/Contracts/HashableInterface.php index 64e5e0c2..e2033926 100644 --- a/src/Altair/Structure/Contracts/HashableInterface.php +++ b/src/Altair/Structure/Contracts/HashableInterface.php @@ -23,7 +23,7 @@ public function hash(); /** * Returns whether this object is considered equal to another. * - * @param $obj + * @param object $obj * * @return bool true if equal, false otherwise. */ diff --git a/src/Altair/Structure/Contracts/MapInterface.php b/src/Altair/Structure/Contracts/MapInterface.php index e1413269..4b19c16f 100644 --- a/src/Altair/Structure/Contracts/MapInterface.php +++ b/src/Altair/Structure/Contracts/MapInterface.php @@ -16,33 +16,47 @@ use Traversable; use UnderflowException; +/** + * @template TKey + * @template TValue + * + * @extends CollectionInterface + */ interface MapInterface extends CollectionInterface { /** * Updates all values by applying a callback function to each value. * * @param callable $callback Accepts two arguments: key and value, should return what the updated value will be. + * + * @return MapInterface */ public function apply(callable $callback): MapInterface; /** * Merge an array of values with the current Map. * - * @param array|Traversable $values + * @param array|Traversable $values + * + * @return MapInterface */ public function merge($values): MapInterface; /** * Intersect. * + * @param MapInterface $map * + * @return MapInterface */ public function intersect(MapInterface $map): MapInterface; /** * Diff. * + * @param MapInterface $map * + * @return MapInterface */ public function diff(MapInterface $map): MapInterface; @@ -52,6 +66,8 @@ public function diff(MapInterface $map): MapInterface; * * @param callable $callback Accepts a key and a value, and returns: true : include the value, false: skip the * value. + * + * @return MapInterface */ public function filter(callable $callback): MapInterface; @@ -61,6 +77,8 @@ public function filter(callable $callback): MapInterface; * The keys will be equal in both maps. * * @param callable $callback Accepts two arguments: key and value, should return what the updated value will be. + * + * @return MapInterface */ public function map(callable $callback): MapInterface; @@ -68,19 +86,26 @@ public function map(callable $callback): MapInterface; * Associates a key with a value, replacing a previous association if there * was one. * + * @param TKey $key + * @param TValue $value * + * @return MapInterface */ public function put(mixed $key, mixed $value): MapInterface; /** * Creates associations for all keys and corresponding values of either an array or iterable object. * - * @param Traversable|array $values + * @param Traversable|array $values + * + * @return MapInterface */ public function putAll($values): MapInterface; /** * Returns a reversed copy of the map. + * + * @return MapInterface */ public function reverse(): MapInterface; @@ -95,6 +120,8 @@ public function reverse(): MapInterface; * If a length is given and is negative, the map will stop that many pairs from the end. * * If a length is not provided, the resulting map will contains all pairs between the offset and the end of the map. + * + * @return MapInterface */ public function slice(int $offset, ?int $length = null): MapInterface; @@ -103,6 +130,8 @@ public function slice(int $offset, ?int $length = null): MapInterface; * comparator. The map will be sorted by value. * * @param callable|null $comparator Accepts two values to be compared. + * + * @return MapInterface */ public function sort(?callable $comparator = null): MapInterface; @@ -111,30 +140,40 @@ public function sort(?callable $comparator = null): MapInterface; * comparator. The map will be sorted by key. * * @param callable|null $comparator Accepts two keys to be compared. + * + * @return MapInterface */ public function ksort(?callable $comparator = null): MapInterface; /** * Merges two maps. * + * @param MapInterface $map * + * @return MapInterface */ public function union(MapInterface $map): MapInterface; /** * XOR two maps. * + * @param MapInterface $map * + * @return MapInterface */ public function xor(MapInterface $map): MapInterface; /** * Returns a sequence of pairs representing all associations. + * + * @return VectorInterface> */ public function pairs(): VectorInterface; /** * Returns a sequence of all the associated values in the Map. + * + * @return VectorInterface */ public function values(): VectorInterface; @@ -143,7 +182,7 @@ public function values(): VectorInterface; * * @throws UnderflowException * - * + * @return PairInterface */ public function first(): PairInterface; @@ -152,7 +191,7 @@ public function first(): PairInterface; * * @throws UnderflowException * - * + * @return PairInterface */ public function last(): PairInterface; @@ -162,25 +201,28 @@ public function last(): PairInterface; * * @throws OutOfRangeException * + * @return PairInterface */ public function skip(int $position): PairInterface; /** * Returns a set of all the keys in the map. + * + * @return SetInterface */ public function keys(): SetInterface; /** * Returns whether an association a given key exists. * - * + * @param TKey $key */ public function hasKey(mixed $key): bool; /** * Returns whether an association for a given value exists. * - * + * @param TValue $value */ public function hasValue(mixed $value): bool; @@ -188,9 +230,11 @@ public function hasValue(mixed $value): bool; * Returns the value associated with a key, or an optional default if the * key is not associated with a value. * + * @param TKey $key + * @param TValue|null $default * * @throws OutOfBoundsException if no default was provided and the key isnot associated with a value. - * @return mixed The associated value or fallback default if provided. + * @return TValue The associated value or fallback default if provided. * */ public function get(mixed $key, mixed $default = null); @@ -215,9 +259,11 @@ public function sum(); /** * Removes a key's association from the map and returns the associated value or a provided default if provided. * + * @param TKey $key + * @param TValue|null $default * * @throws OutOfBoundsException if no default was provided and the key is not associated with a value. - * @return mixed The associated value or fallback default if provided. + * @return TValue The associated value or fallback default if provided. * */ public function remove(mixed $key, mixed $default = null); diff --git a/src/Altair/Structure/Contracts/PairInterface.php b/src/Altair/Structure/Contracts/PairInterface.php index d9710899..675c30e0 100644 --- a/src/Altair/Structure/Contracts/PairInterface.php +++ b/src/Altair/Structure/Contracts/PairInterface.php @@ -11,22 +11,30 @@ namespace Altair\Structure\Contracts; +/** + * @template TKey + * @template TValue + */ interface PairInterface { /** * Check if keys are equal. * - * + * @param TKey $key */ public function equalsKey(mixed $key): bool; /** * Returns a copy of the Pair. + * + * @return PairInterface */ public function copy(): PairInterface; /** * Returns pair as array. + * + * @return array{key: TKey, value: TValue} */ public function toArray(): array; } diff --git a/src/Altair/Structure/Contracts/PriorityNodeInterface.php b/src/Altair/Structure/Contracts/PriorityNodeInterface.php index 5994c3e5..741fcaf1 100644 --- a/src/Altair/Structure/Contracts/PriorityNodeInterface.php +++ b/src/Altair/Structure/Contracts/PriorityNodeInterface.php @@ -14,5 +14,7 @@ /** * * PriorityNodeInterface. + * + * @template TValue */ interface PriorityNodeInterface {} diff --git a/src/Altair/Structure/Contracts/QueueInterface.php b/src/Altair/Structure/Contracts/QueueInterface.php index d6baf74a..dbb52567 100644 --- a/src/Altair/Structure/Contracts/QueueInterface.php +++ b/src/Altair/Structure/Contracts/QueueInterface.php @@ -11,26 +11,33 @@ namespace Altair\Structure\Contracts; +/** + * @template TValue + * + * @extends CollectionInterface + */ interface QueueInterface extends CollectionInterface { /** * Returns the value at the front of the queue without removing it. * - * @return mixed + * @return TValue */ public function peek(); /** * Returns and removes the value at the front of the Queue. * - * @return mixed + * @return TValue */ public function pop(); /** * Pushes zero or more values into the front of the queue. * + * @param TValue ...$values * + * @return QueueInterface */ public function push(mixed ...$values): QueueInterface; } diff --git a/src/Altair/Structure/Contracts/SequenceInterface.php b/src/Altair/Structure/Contracts/SequenceInterface.php index 58b8d812..b9e73178 100644 --- a/src/Altair/Structure/Contracts/SequenceInterface.php +++ b/src/Altair/Structure/Contracts/SequenceInterface.php @@ -15,12 +15,17 @@ use Traversable; use UnderflowException; +/** + * @template TValue + * + * @extends CollectionInterface + */ interface SequenceInterface extends CollectionInterface { /** * Creates a new sequence using the values of either an array or iterable object as initial values. * - * @param array|Traversable|null $values + * @param array|Traversable|null $values */ public function __construct($values = null); @@ -28,12 +33,15 @@ public function __construct($values = null); * Updates every value in the sequence by applying a callback, using the return value as the new value. * * @param callable $callback Accepts the value, returns the new value. + * + * @return SequenceInterface */ public function apply(callable $callback): SequenceInterface; /** * Determines whether the sequence contains all of zero or more values. * + * @param TValue ...$values * * @return bool true if at least one value was provided and the sequence contains all given values, false otherwise. */ @@ -45,12 +53,15 @@ public function contains(mixed ...$values): bool; * * @param callable|null $callback Accepts a value, returns a boolean result: true : include the value, false: skip * the value. + * + * @return SequenceInterface */ public function filter(?callable $callback = null): SequenceInterface; /** * Returns the index of a given value, or false if it could not be found. * + * @param TValue $value * * @return int|bool */ @@ -61,7 +72,7 @@ public function find(mixed $value); * * @throws UnderflowException if the sequence is empty. * - * @return mixed + * @return TValue * */ public function first(); @@ -72,7 +83,7 @@ public function first(); * * @throws OutOfRangeException if the index is not in the range [0, size-1] * - * @return mixed + * @return TValue * */ public function get(int $index); @@ -83,9 +94,11 @@ public function get(int $index); * Each value after the index will be moved one position to the right. * Values may be inserted at an index equal to the size of the sequence. * + * @param TValue ...$values * * @throws OutOfRangeException if the index is not in the range [0, n] * + * @return SequenceInterface */ public function insert(int $index, mixed ...$values): SequenceInterface; @@ -102,7 +115,7 @@ public function join(?string $glue = null): string; * * @throws UnderflowException if the sequence is empty. * - * @return mixed + * @return TValue * */ public function last(); @@ -110,14 +123,16 @@ public function last(); /** * Returns a new sequence using the results of applying a callback to each value. * - * + * @return SequenceInterface */ public function map(callable $callback): SequenceInterface; /** * Returns the result of adding all given values to the sequence. * - * @param array|Traversable $values + * @param array|Traversable $values + * + * @return SequenceInterface */ public function merge($values): SequenceInterface; @@ -126,7 +141,7 @@ public function merge($values): SequenceInterface; * * @throws UnderflowException if the sequence is empty. * - * @return mixed what was the last value in the sequence. + * @return TValue what was the last value in the sequence. * */ public function pop(); @@ -134,7 +149,9 @@ public function pop(); /** * Adds zero or more values to the end of the sequence. * + * @param TValue ...$values * + * @return SequenceInterface */ public function push(mixed ...$values): SequenceInterface; @@ -155,13 +172,15 @@ public function reduce(callable $callback, $initial = null); * * @throws OutOfRangeException if the index is not in the range [0, size-1] * - * @return mixed the removed value. + * @return TValue the removed value. * */ public function remove(int $index); /** * Reverses the sequence in-place. + * + * @return SequenceInterface */ public function reverse(): SequenceInterface; @@ -171,15 +190,19 @@ public function reverse(): SequenceInterface; * positive, or 'pop' and 'unshift' if negative. * * @param int $rotations The number of rotations (can be negative). + * + * @return SequenceInterface */ public function rotate(int $rotations): SequenceInterface; /** * Replaces the value at a given index in the sequence with a new value. * + * @param TValue $value * * @throws OutOfRangeException if the index is not in the range [0, size-1] * + * @return SequenceInterface */ public function set(int $index, mixed $value): SequenceInterface; @@ -188,7 +211,7 @@ public function set(int $index, mixed $value): SequenceInterface; * * @throws UnderflowException if the sequence was empty. * - * @return mixed what was the first value in the sequence. + * @return TValue what was the first value in the sequence. * */ public function shift(); @@ -205,6 +228,8 @@ public function shift(); * * If a length is not provided, the resulting sequence will contain all values between the index and the end of the * sequence. + * + * @return SequenceInterface */ public function slice(int $index, ?int $length = null): SequenceInterface; @@ -213,6 +238,8 @@ public function slice(int $index, ?int $length = null): SequenceInterface; * comparator. Natural ordering will be used if a comparator is not given. * * @param callable|null $comparator Accepts two values to be compared. Should return the result of a <=> b. + * + * @return SequenceInterface */ public function sort(?callable $comparator = null): SequenceInterface; @@ -226,7 +253,9 @@ public function sum(); /** * Adds zero or more values to the front of the sequence. * + * @param TValue ...$values * + * @return SequenceInterface */ public function unshift(mixed ...$values): SequenceInterface; } diff --git a/src/Altair/Structure/Contracts/SetInterface.php b/src/Altair/Structure/Contracts/SetInterface.php index b4e67c0e..98e2a9b2 100644 --- a/src/Altair/Structure/Contracts/SetInterface.php +++ b/src/Altair/Structure/Contracts/SetInterface.php @@ -14,12 +14,19 @@ use OutOfRangeException; use Traversable; +/** + * @template TValue + * + * @extends CollectionInterface + */ interface SetInterface extends CollectionInterface { /** * Adds zero or more values to the set. * + * @param TValue ...$values * + * @return SetInterface */ public function add(mixed ...$values): SetInterface; @@ -29,6 +36,8 @@ public function add(mixed ...$values): SetInterface; * * @param int $capacity The number of values for which capacity should be allocated. Capacity will stay the same if * this value is less than or equal to the current capacity. + * + * @return SetInterface */ public function allocate(int $capacity): SetInterface; @@ -37,7 +46,9 @@ public function allocate(int $capacity): SetInterface; * * Formally: A \ B = {x ∈ A | x ∉ B} * + * @param SetInterface $set * + * @return SetInterface */ public function diff(SetInterface $set): SetInterface; @@ -47,7 +58,9 @@ public function diff(SetInterface $set): SetInterface; * * Formally: A ⊖ B = {x : x ∈ (A \ B) ∪ (B \ A)} * + * @param SetInterface $set * + * @return SetInterface */ public function xor(SetInterface $set): SetInterface; @@ -59,6 +72,7 @@ public function capacity(): int; /** * Determines whether the set contains all of zero or more values. * + * @param TValue ...$values * * @return bool true if at least one value was provided and the set contains all given values, false otherwise. */ @@ -70,20 +84,22 @@ public function contains(mixed ...$values): bool; * * @param callable|null $callback Accepts a value, returns a boolean: true : include the value, false: skip the * value. + * + * @return SetInterface */ public function filter(?callable $callback = null): SetInterface; /** * Returns the first value in the set. * - * @return mixed the first value in the set. + * @return TValue the first value in the set. */ public function first(); /** * Returns the last value in the set. * - * @return mixed the last value in the set. + * @return TValue the last value in the set. */ public function last(); @@ -93,7 +109,7 @@ public function last(); * * @throws OutOfRangeException * - * @return mixed|null + * @return TValue|null * */ public function get(int $position); @@ -106,7 +122,9 @@ public function get(int $position); * * Formally: A ∩ B = {x : x ∈ A ∧ x ∈ B} * + * @param SetInterface $set * + * @return SetInterface */ public function intersect(SetInterface $set): SetInterface; @@ -130,11 +148,15 @@ public function reduce(callable $callback, $initial = null); /** * Removes zero or more values from the set. + * + * @param TValue ...$values */ - public function remove(mixed ...$values); + public function remove(mixed ...$values): void; /** * Returns a reversed copy of the set. + * + * @return SetInterface */ public function reverse(): SetInterface; @@ -147,6 +169,8 @@ public function reverse(): SetInterface; * it. If the requested length results in an overflow, only values up to the end of the set will be included. * If a length is given and is negative, the set will stop that many values from the end. If a length is not * provided, the resulting set will contains all values between the offset and the end of the set. + * + * @return SetInterface */ public function slice(int $offset, ?int $length = null): SetInterface; @@ -154,13 +178,17 @@ public function slice(int $offset, ?int $length = null): SetInterface; * Sorts the set in-place, based on an optional callable comparator. * * @param callable|null $comparator Accepts two values to be compared. Should return the result of a <=> b. + * + * @return SetInterface */ public function sort(?callable $comparator = null): SetInterface; /** * Returns the result of adding all given values to the set. * - * @param array|Traversable $values + * @param array|Traversable $values + * + * @return SetInterface */ public function merge($values): SetInterface; @@ -177,12 +205,16 @@ public function sum(); * * Formally: A ∪ B = {x: x ∈ A ∨ x ∈ B} * + * @param SetInterface $set * + * @return SetInterface */ public function union(SetInterface $set): SetInterface; /** * Returns the MapInterface used internally to keep its values. + * + * @return MapInterface */ public function getMap(): MapInterface; } diff --git a/src/Altair/Structure/Contracts/StackInterface.php b/src/Altair/Structure/Contracts/StackInterface.php index 6243270a..6093f766 100644 --- a/src/Altair/Structure/Contracts/StackInterface.php +++ b/src/Altair/Structure/Contracts/StackInterface.php @@ -13,6 +13,11 @@ use UnderflowException; +/** + * @template TValue + * + * @extends CollectionInterface + */ interface StackInterface extends CollectionInterface { /** @@ -20,7 +25,7 @@ interface StackInterface extends CollectionInterface * * @throws UnderflowException if the stack is empty. * - * @return mixed + * @return TValue * */ public function peek(); @@ -30,7 +35,7 @@ public function peek(); * * @throws UnderflowException if the stack is empty. * - * @return mixed + * @return TValue * */ public function pop(); @@ -38,7 +43,9 @@ public function pop(); /** * Pushes zero or more values into the front of the queue. * + * @param TValue ...$values * + * @return StackInterface */ public function push(mixed ...$values): StackInterface; } diff --git a/src/Altair/Structure/Contracts/VectorInterface.php b/src/Altair/Structure/Contracts/VectorInterface.php index 89a17e36..c53248b0 100644 --- a/src/Altair/Structure/Contracts/VectorInterface.php +++ b/src/Altair/Structure/Contracts/VectorInterface.php @@ -11,6 +11,11 @@ namespace Altair\Structure\Contracts; +/** + * @template TValue + * + * @extends SequenceInterface + */ interface VectorInterface extends SequenceInterface { public const MIN_VECTOR_CAPACITY = 10; diff --git a/src/Altair/Structure/Deque.php b/src/Altair/Structure/Deque.php index 19970c36..fd55ebb0 100644 --- a/src/Altair/Structure/Deque.php +++ b/src/Altair/Structure/Deque.php @@ -17,6 +17,7 @@ use Altair\Structure\Traits\SquaredCapacityTrait; use ArrayAccess; use IteratorAggregate; +use Traversable; /** * Deque. @@ -31,14 +32,23 @@ * ((head + position) % capacity). * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr + * + * @template TValue + * + * @implements SequenceInterface + * @implements IteratorAggregate + * @implements ArrayAccess */ class Deque implements IteratorAggregate, ArrayAccess, SequenceInterface, CapacityInterface { + /** @use SequenceTrait */ use SequenceTrait; use SquaredCapacityTrait; /** * @inheritDoc + * + * @param array|Traversable|null $values */ public function __construct($values = null) { diff --git a/src/Altair/Structure/Map.php b/src/Altair/Structure/Map.php index a20a8318..380bcc0b 100644 --- a/src/Altair/Structure/Map.php +++ b/src/Altair/Structure/Map.php @@ -19,6 +19,7 @@ use Altair\Structure\Traits\CollectionTrait; use Altair\Structure\Traits\SquaredCapacityTrait; use ArrayAccess; +use Generator; use IteratorAggregate; use OutOfBoundsException; use OutOfRangeException; @@ -35,16 +36,34 @@ * insertion order is preserved. * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr + * + * @template TKey + * @template TValue + * + * @implements MapInterface + * @implements IteratorAggregate + * @implements ArrayAccess */ class Map implements IteratorAggregate, ArrayAccess, MapInterface, CapacityInterface { + /** @use CollectionTrait */ use CollectionTrait; use SquaredCapacityTrait; + /** + * Internal storage of key/value associations as a list of pairs. + * + * Typed against the concrete Pair (not PairInterface) so the public + * $key/$value properties resolve; the Map only ever stores Pair instances. + * + * @var array> + */ + protected $internal = []; + /** * Creates an instance using the values of an array or Traversable object. * - * @param array|Traversable|MapInterface|null $values + * @param array|Traversable|MapInterface|null $values */ public function __construct($values = []) { @@ -55,6 +74,8 @@ public function __construct($values = []) /** * Debug Info. + * + * @return array> */ public function __debugInfo() { @@ -63,6 +84,8 @@ public function __debugInfo() /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function apply(callable $callback): MapInterface @@ -76,6 +99,10 @@ public function apply(callable $callback): MapInterface /** * {@inheritDoc} + * + * @param array|Traversable $values + * + * @return MapInterface */ #[Override] public function merge($values): MapInterface @@ -88,6 +115,10 @@ public function merge($values): MapInterface /** * {@inheritDoc} + * + * @param MapInterface $map + * + * @return MapInterface */ #[Override] public function intersect(MapInterface $map): MapInterface @@ -99,6 +130,10 @@ public function intersect(MapInterface $map): MapInterface /** * {@inheritDoc} + * + * @param MapInterface $map + * + * @return MapInterface */ #[Override] public function diff(MapInterface $map): MapInterface @@ -110,6 +145,8 @@ public function diff(MapInterface $map): MapInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function filter(?callable $callback = null): MapInterface @@ -127,6 +164,8 @@ public function filter(?callable $callback = null): MapInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function map(callable $callback): MapInterface @@ -142,6 +181,11 @@ public function map(callable $callback): MapInterface /** * {@inheritDoc} + * + * @param TKey $key + * @param TValue $value + * + * @return MapInterface */ #[Override] public function put($key, $value): MapInterface @@ -160,6 +204,10 @@ public function put($key, $value): MapInterface /** * {@inheritDoc} + * + * @param array|Traversable $values + * + * @return MapInterface */ #[Override] public function putAll($values): MapInterface @@ -173,6 +221,8 @@ public function putAll($values): MapInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function reverse(): MapInterface @@ -184,6 +234,8 @@ public function reverse(): MapInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function slice(int $offset, ?int $length = null): MapInterface @@ -195,6 +247,8 @@ public function slice(int $offset, ?int $length = null): MapInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function sort(?callable $comparator = null): MapInterface @@ -218,6 +272,8 @@ public function sort(?callable $comparator = null): MapInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function ksort(?callable $comparator = null): MapInterface @@ -241,6 +297,10 @@ public function ksort(?callable $comparator = null): MapInterface /** * {@inheritDoc} + * + * @param MapInterface $map + * + * @return MapInterface */ #[Override] public function union(MapInterface $map): MapInterface @@ -250,6 +310,10 @@ public function union(MapInterface $map): MapInterface /** * {@inheritDoc} + * + * @param MapInterface $map + * + * @return MapInterface */ #[Override] public function xor(MapInterface $map): MapInterface @@ -261,13 +325,14 @@ public function xor(MapInterface $map): MapInterface /** * {@inheritDoc} + * + * @return VectorInterface> */ #[Override] public function pairs(): VectorInterface { $sequence = new Vector(); - /** @var Pair $pair */ foreach ($this->internal as $pair) { $sequence[] = $pair->copy(); } @@ -277,6 +342,8 @@ public function pairs(): VectorInterface /** * {@inheritDoc} + * + * @return VectorInterface */ #[Override] public function values(): VectorInterface @@ -295,7 +362,7 @@ public function values(): VectorInterface * * @throws UnderflowException * - * + * @return Pair */ #[Override] public function first(): PairInterface @@ -309,6 +376,8 @@ public function first(): PairInterface /** * {@inheritDoc} + * + * @return Pair */ #[Override] public function last(): PairInterface @@ -322,6 +391,8 @@ public function last(): PairInterface /** * {@inheritDoc} + * + * @return Pair */ #[Override] public function skip(int $position): PairInterface @@ -330,7 +401,6 @@ public function skip(int $position): PairInterface throw new OutOfRangeException('Out of range'); } - /** @var PairInterface $pair */ $pair = $this->internal[$position]; return $pair->copy(); @@ -338,6 +408,8 @@ public function skip(int $position): PairInterface /** * {@inheritDoc} + * + * @return SetInterface */ #[Override] public function keys(): SetInterface @@ -353,6 +425,8 @@ public function keys(): SetInterface /** * {@inheritDoc} + * + * @param TKey $key */ #[Override] public function hasKey($key): bool @@ -362,6 +436,8 @@ public function hasKey($key): bool /** * {@inheritDoc} + * + * @param TValue $value */ #[Override] public function hasValue($value): bool @@ -371,6 +447,11 @@ public function hasValue($value): bool /** * {@inheritDoc} + * + * @param TKey $key + * @param TValue|null $default + * + * @return TValue */ #[Override] public function get($key, $default = null) @@ -408,6 +489,11 @@ public function sum() /** * {@inheritDoc} + * + * @param TKey $key + * @param TValue|null $default + * + * @return TValue */ #[Override] public function remove($key, $default = null) @@ -423,6 +509,8 @@ public function remove($key, $default = null) /** * {@inheritDoc} + * + * @return array */ #[Override] public function toArray(): array @@ -432,6 +520,8 @@ public function toArray(): array /** * Get iterator. + * + * @return Generator */ #[ReturnTypeWillChange] #[Override] @@ -444,10 +534,13 @@ public function getIterator() /** * {@inheritDoc} + * + * @param TKey $offset + * @param TValue $value */ #[ReturnTypeWillChange] #[Override] - public function offsetSet($offset, $value): void + public function offsetSet(mixed $offset, mixed $value): void { $this->put($offset, $value); } @@ -455,11 +548,15 @@ public function offsetSet($offset, $value): void /** * {@inheritDoc} * + * @param TKey $offset + * * @throws OutOfBoundsException + * + * @return TValue */ #[ReturnTypeWillChange] #[Override] - public function &offsetGet($offset) + public function &offsetGet(mixed $offset) { $pair = $this->lookupKey($offset); @@ -493,11 +590,12 @@ public function offsetExists($offset) /** * Returns item if a key is found. * + * @param TKey $key * + * @return Pair|null */ protected function lookupKey(mixed $key): ?PairInterface { - /** @var PairInterface $pair */ foreach ($this->internal as $pair) { if ($pair->equalsKey($key)) { return $pair; @@ -510,7 +608,9 @@ protected function lookupKey(mixed $key): ?PairInterface /** * Returns item if a value is found. * + * @param TValue $value * + * @return Pair|null */ protected function lookupValue(mixed $value): ?PairInterface { @@ -526,12 +626,10 @@ protected function lookupValue(mixed $value): ?PairInterface /** * Removes an Map item at specified position. * - * - * @return mixed + * @return TValue */ protected function delete(int $position) { - /** @var PairInterface $pair */ $pair = $this->internal[$position]; $value = $pair->value; @@ -545,7 +643,9 @@ protected function delete(int $position) /** * Converts pairs to array. * - * @param $pairs + * @param iterable> $pairs + * + * @return array */ protected function pairsToArray($pairs): array { diff --git a/src/Altair/Structure/Pair.php b/src/Altair/Structure/Pair.php index 31b22840..abf3821b 100644 --- a/src/Altair/Structure/Pair.php +++ b/src/Altair/Structure/Pair.php @@ -22,13 +22,25 @@ /** * A pair which represents a key, and an associated value. * + * @template TKey + * @template TValue + * + * @implements PairInterface */ class Pair implements PairInterface, JsonSerializable, Stringable { /** * Constructor. + * + * @param TKey $key + * @param TValue $value */ - public function __construct(public mixed $key = null, public mixed $value = null) {} + public function __construct( + /** @var TKey */ + public mixed $key = null, + /** @var TValue */ + public mixed $value = null, + ) {} /** * This allows unset($pair->key) to not completely remove the property, @@ -51,7 +63,7 @@ public function __get(mixed $name) /** * Debug Info. * - * @return array + * @return array{key: TKey, value: TValue} */ public function __debugInfo() { @@ -69,6 +81,8 @@ public function __toString(): string /** * {@inheritDoc} + * + * @param TKey $key */ #[Override] public function equalsKey($key): bool @@ -82,6 +96,8 @@ public function equalsKey($key): bool /** * Returns a copy of the Pair. + * + * @return static */ #[Override] public function copy(): PairInterface @@ -91,6 +107,8 @@ public function copy(): PairInterface /** * {@inheritDoc} + * + * @return array{key: TKey, value: TValue} */ #[Override] public function toArray(): array @@ -100,6 +118,8 @@ public function toArray(): array /** * {@inheritDoc} + * + * @return array{key: TKey, value: TValue} */ #[ReturnTypeWillChange] #[Override] diff --git a/src/Altair/Structure/PriorityNode.php b/src/Altair/Structure/PriorityNode.php index 50b42a6d..2f1c1c98 100644 --- a/src/Altair/Structure/PriorityNode.php +++ b/src/Altair/Structure/PriorityNode.php @@ -18,6 +18,10 @@ * PriorityNode * * A node which represents a value, priority and a stamp on a PriorityQueue + * + * @template TValue + * + * @implements PriorityNodeInterface */ class PriorityNode implements PriorityNodeInterface { @@ -33,6 +37,8 @@ class PriorityNode implements PriorityNodeInterface /** * PriorityNode constructor. + * + * @param TValue $value */ public function __construct(public mixed $value, int $priority, int $stamp) { diff --git a/src/Altair/Structure/PriorityQueue.php b/src/Altair/Structure/PriorityQueue.php index 492a0bfa..330822aa 100644 --- a/src/Altair/Structure/PriorityQueue.php +++ b/src/Altair/Structure/PriorityQueue.php @@ -15,6 +15,7 @@ use Altair\Structure\Contracts\PriorityNodeInterface; use Altair\Structure\Traits\CollectionTrait; use Altair\Structure\Traits\SquaredCapacityTrait; +use Generator; use IteratorAggregate; use Override; use ReturnTypeWillChange; @@ -29,23 +30,36 @@ * destructive, equivalent to successive pop operations until the queue is empty. Implemented using a max heap. * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr + * + * @template TValue + * + * @implements CollectionInterface + * @implements IteratorAggregate */ class PriorityQueue implements IteratorAggregate, CollectionInterface { + /** @use CollectionTrait */ use CollectionTrait; use SquaredCapacityTrait; /** - * @var PriorityNodeInterface[] + * Typed against the concrete PriorityNode (not the interface) so its public + * $value/$priority/$stamp properties resolve; the queue only ever stores + * PriorityNode instances. + * + * @var array> */ protected $heap = []; + /** + * @var int + */ protected $stamp = 0; /** * Initializes a new priority queue. * - * @param array|null $heap + * @param array>|null $heap * @param int|null $stamp */ public function __construct($heap = [], $stamp = 0) @@ -56,6 +70,8 @@ public function __construct($heap = [], $stamp = 0) /** * {@inheritDoc} + * + * @return self */ #[Override] public function copy(): \Altair\Structure\PriorityQueue @@ -74,7 +90,9 @@ public function count(): int } /** - * {@inheritDoc} + * Returns the value with the highest priority without removing it. + * + * @return TValue */ public function peek() { @@ -88,7 +106,7 @@ public function peek() /** * Returns and removes the value with the highest priority in the queue. * - * @return mixed + * @return TValue */ public function pop() { @@ -116,6 +134,8 @@ public function pop() /** * Pushes a value into the queue, with a specified priority. + * + * @param TValue $value */ public function push(mixed $value, int $priority): void { @@ -128,6 +148,8 @@ public function push(mixed $value, int $priority): void /** * {@inheritDoc} + * + * @return array */ #[Override] public function toArray(): array @@ -146,6 +168,8 @@ public function toArray(): array /** * Get iterator. + * + * @return Generator */ #[ReturnTypeWillChange] #[Override] @@ -246,6 +270,8 @@ protected function siftUp(int $leaf): void /** * Set Root. + * + * @param PriorityNodeInterface $node */ protected function setRoot(PriorityNodeInterface $node): void { @@ -254,6 +280,8 @@ protected function setRoot(PriorityNodeInterface $node): void /** * Get Root. + * + * @return PriorityNode */ protected function getRoot(): PriorityNodeInterface { diff --git a/src/Altair/Structure/Queue.php b/src/Altair/Structure/Queue.php index 3e4fa446..dc2274f8 100644 --- a/src/Altair/Structure/Queue.php +++ b/src/Altair/Structure/Queue.php @@ -16,6 +16,7 @@ use Altair\Structure\Traits\CollectionTrait; use ArrayAccess; use Error; +use Generator; use IteratorAggregate; use OutOfBoundsException; use Override; @@ -29,15 +30,33 @@ * and iterates in that order, destructively. * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr + * + * @template TValue + * + * @implements QueueInterface + * @implements IteratorAggregate + * @implements ArrayAccess */ class Queue implements IteratorAggregate, ArrayAccess, QueueInterface, CapacityInterface { + /** @use CollectionTrait */ use CollectionTrait; + /** + * Backed by a Deque. The empty-array default is required for trait property + * compatibility (CollectionTrait declares `$internal = []`) and is replaced + * with a Deque in the constructor before any method is invoked. + * + * @var Deque + * + * @phpstan-ignore property.defaultValue + */ + protected $internal = []; + /** * Creates an instance using the values of an array or Traversable object. * - * @param array|Traversable|Queue $values + * @param array|Traversable|Queue|null $values */ public function __construct($values = null) { @@ -46,6 +65,8 @@ public function __construct($values = null) /** * {@inheritDoc} + * + * @return TValue */ #[Override] public function peek() @@ -55,6 +76,8 @@ public function peek() /** * {@inheritDoc} + * + * @return TValue */ #[Override] public function pop() @@ -64,6 +87,10 @@ public function pop() /** * {@inheritDoc} + * + * @param TValue ...$values + * + * @return QueueInterface */ #[Override] public function push(...$values): QueueInterface @@ -75,6 +102,8 @@ public function push(...$values): QueueInterface /** * {@inheritDoc} + * + * @return QueueInterface */ #[Override] public function allocate(int $capacity): QueueInterface @@ -104,6 +133,8 @@ public function copy(): static /** * {@inheritDoc} + * + * @return array */ #[Override] public function toArray(): array @@ -113,6 +144,8 @@ public function toArray(): array /** * Get iterator. + * + * @return Generator */ #[ReturnTypeWillChange] #[Override] @@ -126,11 +159,13 @@ public function getIterator() /** * {@inheritDoc} * + * @param TValue $value + * * @throws OutOfBoundsException */ #[ReturnTypeWillChange] #[Override] - public function offsetSet($offset, $value): void + public function offsetSet($offset, mixed $value): void { if ($offset === null) { $this->push($value); diff --git a/src/Altair/Structure/Set.php b/src/Altair/Structure/Set.php index 38778d7c..81357cb6 100644 --- a/src/Altair/Structure/Set.php +++ b/src/Altair/Structure/Set.php @@ -17,6 +17,7 @@ use Altair\Structure\Traits\CollectionTrait; use ArrayAccess; use Error; +use Generator; use IteratorAggregate; use OutOfBoundsException; use Override; @@ -35,28 +36,52 @@ * Set can be sorted in O(n * log(n)) time whenever it needs to be, just like a Map and an array. * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr + * + * @template TValue + * + * @implements SetInterface + * @implements IteratorAggregate + * @implements ArrayAccess */ class Set implements IteratorAggregate, ArrayAccess, SetInterface, CapacityInterface { + /** @use CollectionTrait */ use CollectionTrait; + /** + * Set values are stored as the keys of an internal Map; the associated + * value is always null. Typed against the concrete Map so its capacity API + * and pair access resolve. The empty-array default is required for trait + * property compatibility (CollectionTrait declares `$internal = []`) and is + * replaced with a Map in the constructor before any method is invoked. + * + * @var Map + * + * @phpstan-ignore property.defaultValue + */ + protected $internal = []; + /** * Creates a new set using the values of an array or Traversable object. * The keys of either will not be preserved. * - * @param array|Traversable|null $values + * @param array|Traversable|null $values */ public function __construct($values = null) { $this->internal = new Map(); if (\func_num_args() !== 0) { - $this->add(...$values); + $this->add(...$this->normalizeItems($values ?? [])); } } /** * {@inheritDoc} + * + * @param TValue ...$values + * + * @return SetInterface */ #[Override] public function add(...$values): SetInterface @@ -70,6 +95,8 @@ public function add(...$values): SetInterface /** * {@inheritDoc} + * + * @return SetInterface */ #[Override] public function allocate(int $capacity): SetInterface @@ -81,6 +108,10 @@ public function allocate(int $capacity): SetInterface /** * {@inheritDoc} + * + * @param SetInterface $set + * + * @return SetInterface */ #[Override] public function diff(SetInterface $set): SetInterface @@ -90,6 +121,10 @@ public function diff(SetInterface $set): SetInterface /** * {@inheritDoc} + * + * @param SetInterface $set + * + * @return SetInterface */ #[Override] public function xor(SetInterface $set): SetInterface @@ -108,6 +143,8 @@ public function capacity(): int /** * {@inheritDoc} + * + * @param TValue ...$values */ #[Override] public function contains(...$values): bool @@ -123,6 +160,8 @@ public function contains(...$values): bool /** * {@inheritDoc} + * + * @return SetInterface */ #[Override] public function filter(?callable $callback = null): SetInterface @@ -133,7 +172,7 @@ public function filter(?callable $callback = null): SetInterface /** * Returns the first value in the set. * - * @return mixed the first value in the set. + * @return TValue the first value in the set. */ #[Override] public function first() @@ -144,7 +183,7 @@ public function first() /** * Returns the last value in the set. * - * @return mixed the last value in the set. + * @return TValue the last value in the set. */ #[Override] public function last() @@ -154,6 +193,8 @@ public function last() /** * {@inheritDoc} + * + * @return TValue */ #[Override] public function get(int $position) @@ -163,6 +204,10 @@ public function get(int $position) /** * {@inheritDoc} + * + * @param SetInterface $set + * + * @return SetInterface */ #[Override] public function intersect(SetInterface $set): SetInterface @@ -196,6 +241,8 @@ public function reduce(callable $callback, $initial = null) /** * {@inheritDoc} + * + * @param TValue ...$values */ #[Override] public function remove(...$values): void @@ -207,6 +254,8 @@ public function remove(...$values): void /** * Returns a reversed copy of the set. + * + * @return SetInterface */ #[Override] public function reverse(): SetInterface @@ -218,6 +267,8 @@ public function reverse(): SetInterface /** * {@inheritDoc} + * + * @return SetInterface */ #[Override] public function slice(int $offset, ?int $length = null): SetInterface @@ -227,6 +278,8 @@ public function slice(int $offset, ?int $length = null): SetInterface /** * {@inheritDoc} + * + * @return SetInterface */ #[Override] public function sort(?callable $comparator = null): SetInterface @@ -237,7 +290,9 @@ public function sort(?callable $comparator = null): SetInterface /** * Returns the result of adding all given values to the set. * - * @param array|Traversable $values + * @param array|Traversable $values + * + * @return SetInterface */ #[Override] public function merge($values): SetInterface @@ -268,7 +323,9 @@ public function sum(): float|int * * Formally: A ∪ B = {x: x ∈ A ∨ x ∈ B} * + * @param SetInterface $set * + * @return SetInterface */ #[Override] public function union(SetInterface $set): SetInterface @@ -288,6 +345,8 @@ public function union(SetInterface $set): SetInterface /** * {@inheritDoc} + * + * @return MapInterface */ #[Override] public function getMap(): MapInterface @@ -306,6 +365,8 @@ public function isEmpty(): bool /** * {@inheritDoc} + * + * @return array */ #[Override] public function toArray(): array @@ -315,6 +376,8 @@ public function toArray(): array /** * Get iterator. + * + * @return Generator */ #[ReturnTypeWillChange] #[Override] @@ -328,11 +391,13 @@ public function getIterator() /** * {@inheritDoc} * + * @param TValue $value + * * @throws OutOfBoundsException */ #[ReturnTypeWillChange] #[Override] - public function offsetSet($offset, $value): void + public function offsetSet($offset, mixed $value): void { if ($offset === null) { $this->add($value); diff --git a/src/Altair/Structure/Stack.php b/src/Altair/Structure/Stack.php index 6ea340b6..9de306b8 100644 --- a/src/Altair/Structure/Stack.php +++ b/src/Altair/Structure/Stack.php @@ -31,15 +31,33 @@ * internally. * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr + * + * @template TValue + * + * @implements StackInterface + * @implements IteratorAggregate + * @implements ArrayAccess */ class Stack implements IteratorAggregate, ArrayAccess, StackInterface, CapacityInterface { + /** @use CollectionTrait */ use CollectionTrait; + /** + * Backed by a Vector. The empty-array default is required for trait property + * compatibility (CollectionTrait declares `$internal = []`) and is replaced + * with a Vector in the constructor before any method is invoked. + * + * @var Vector + * + * @phpstan-ignore property.defaultValue + */ + protected $internal = []; + /** * Creates an instance using the values of an array or Traversable object. * - * @param array|Traversable $values + * @param array|Traversable|null $values */ public function __construct($values = null) { @@ -48,15 +66,19 @@ public function __construct($values = null) /** * {@inheritDoc} + * + * @return TValue */ #[Override] - public function peek() + public function peek(): mixed { return $this->internal->last(); } /** * {@inheritDoc} + * + * @return TValue */ #[Override] public function pop() @@ -66,6 +88,10 @@ public function pop() /** * {@inheritDoc} + * + * @param TValue ...$values + * + * @return StackInterface */ #[Override] public function push(...$values): StackInterface @@ -116,6 +142,8 @@ public function capacity(): int /** * {@inheritDoc} + * + * @return array */ #[Override] public function toArray(): array @@ -124,7 +152,7 @@ public function toArray(): array } /** - * @return Generator + * @return Generator */ #[ReturnTypeWillChange] #[Override] @@ -138,11 +166,13 @@ public function getIterator() /** * {@inheritDoc} * + * @param TValue $value + * * @throws OutOfBoundsException */ #[ReturnTypeWillChange] #[Override] - public function offsetSet($offset, $value): void + public function offsetSet($offset, mixed $value): void { if ($offset === null) { $this->push($value); diff --git a/src/Altair/Structure/Traits/CollectionTrait.php b/src/Altair/Structure/Traits/CollectionTrait.php index 8ebf1b34..29dee873 100644 --- a/src/Altair/Structure/Traits/CollectionTrait.php +++ b/src/Altair/Structure/Traits/CollectionTrait.php @@ -19,15 +19,32 @@ /** * Common to structures that implement the base collection interface. + * + * @template TKey + * @template TValue */ trait CollectionTrait { + /** + * Internal storage for sequence-backed collections, keyed by position. + * + * Key-backed (Map) and adapter (Set, Stack, Queue) collections redeclare + * this property with a narrower @var in their own class because they store + * PairInterface instances or a delegate collection object instead. For the + * adapters the empty-array default cannot match their narrowed delegate + * type, but it is always overwritten in the constructor before use; PHPStan + * evaluates the default per using-class, so the check is suppressed here. + * + * @var array + * + * @phpstan-ignore property.defaultValue + */ protected $internal = []; /** * Creates an instance using the values of an array or Traversable object. * - * @param array|Traversable|CollectionInterface|null $values + * @param array|Traversable|CollectionInterface|null $values */ public function __construct($values = []) { @@ -39,7 +56,7 @@ public function __construct($values = []) /** * Invoked when calling var_dump. * - * @return array + * @return array */ public function __debugInfo() { @@ -124,19 +141,28 @@ public function toJson(int $options = 0, $depth = 512): string * * The format of the returned array is implementation-dependent. Some implementations may throw an exception if an * array representation could not be created (for example when object are used as keys). + * + * @return array */ abstract public function toArray(): array; /** * Pushes all values of either an array or traversable object. + * + * @param iterable $values */ protected function pushAll(mixed $values): void { foreach ($values as $value) { + // Adapter collections (Set, Stack, Queue) narrow $internal to a + // delegate object and never call pushAll, so the array-append and + // adjustCapacity() call below are unreachable in those contexts. + // @phpstan-ignore offsetAssign.dimType $this->internal[] = $value; } if ($this instanceof CapacityInterface) { + // @phpstan-ignore method.notFound $this->adjustCapacity(); } } @@ -144,7 +170,7 @@ protected function pushAll(mixed $values): void /** * Results array of items from Collections or array. * - * + * @return array */ protected function normalizeItems(mixed $items): array { diff --git a/src/Altair/Structure/Traits/SequenceTrait.php b/src/Altair/Structure/Traits/SequenceTrait.php index 35d0fb63..07823d16 100644 --- a/src/Altair/Structure/Traits/SequenceTrait.php +++ b/src/Altair/Structure/Traits/SequenceTrait.php @@ -16,17 +16,25 @@ use Generator; use OutOfRangeException; use ReturnTypeWillChange; +use Traversable; use UnderflowException; /** * Common functionality of all structures that implement 'Sequence'. + * + * @template TValue + * + * @use CollectionTrait */ trait SequenceTrait { + /** @use CollectionTrait */ use CollectionTrait; /** * {@inheritDoc} + * + * @return array */ public function toArray(): array { @@ -35,6 +43,8 @@ public function toArray(): array /** * {@inheritDoc} + * + * @return SequenceInterface */ public function apply(callable $callback): SequenceInterface { @@ -47,6 +57,10 @@ public function apply(callable $callback): SequenceInterface /** * {@inheritDoc} + * + * @param array|Traversable $values + * + * @return SequenceInterface */ public function merge($values): SequenceInterface { @@ -59,8 +73,10 @@ public function merge($values): SequenceInterface /** * {@inheritDoc} + * + * @param TValue ...$values */ - public function contains(...$values): bool + public function contains(mixed ...$values): bool { foreach ($values as $value) { if ($this->find($value) === false) { @@ -73,6 +89,8 @@ public function contains(...$values): bool /** * {@inheritDoc} + * + * @return SequenceInterface */ public function filter(?callable $callback = null): SequenceInterface { @@ -81,14 +99,18 @@ public function filter(?callable $callback = null): SequenceInterface /** * {@inheritDoc} + * + * @param TValue $value */ - public function find($value): int|string|false + public function find(mixed $value): int|string|false { return array_search($value, $this->internal, true); } /** * {@inheritDoc} + * + * @return TValue */ public function first() { @@ -101,6 +123,8 @@ public function first() /** * {@inheritDoc} + * + * @return TValue */ public function get(int $index) { @@ -111,8 +135,12 @@ public function get(int $index) /** * {@inheritDoc} + * + * @param TValue ...$values + * + * @return SequenceInterface */ - public function insert(int $index, ...$values): SequenceInterface + public function insert(int $index, mixed ...$values): SequenceInterface { if ($index < 0 || $index > \count($this->internal)) { throw new OutOfRangeException('Out of bounds'); @@ -133,6 +161,8 @@ public function join(?string $glue = null): string /** * {@inheritDoc} + * + * @return TValue */ public function last(): mixed { @@ -145,6 +175,8 @@ public function last(): mixed /** * {@inheritDoc} + * + * @return SequenceInterface */ public function map(callable $callback): SequenceInterface { @@ -153,6 +185,8 @@ public function map(callable $callback): SequenceInterface /** * {@inheritDoc} + * + * @return TValue */ public function pop() { @@ -170,8 +204,12 @@ public function pop() /** * {@inheritDoc} + * + * @param TValue ...$values + * + * @return SequenceInterface */ - public function push(...$values): SequenceInterface + public function push(mixed ...$values): SequenceInterface { $this->pushAll($values); @@ -188,6 +226,8 @@ public function reduce(callable $callback, $initial = null) /** * {@inheritDoc} + * + * @return TValue */ public function remove(int $index) { @@ -203,6 +243,8 @@ public function remove(int $index) /** * {@inheritDoc} + * + * @return SequenceInterface */ public function reverse(): SequenceInterface { @@ -211,6 +253,8 @@ public function reverse(): SequenceInterface /** * {@inheritDoc} + * + * @return SequenceInterface */ public function rotate(int $rotations): SequenceInterface { @@ -230,8 +274,12 @@ public function rotate(int $rotations): SequenceInterface /** * {@inheritDoc} + * + * @param TValue $value + * + * @return SequenceInterface */ - public function set(int $index, $value): SequenceInterface + public function set(int $index, mixed $value): SequenceInterface { $this->checkRange($index); $this->internal[$index] = $value; @@ -241,6 +289,8 @@ public function set(int $index, $value): SequenceInterface /** * {@inheritDoc} + * + * @return TValue */ public function shift() { @@ -258,6 +308,8 @@ public function shift() /** * {@inheritDoc} + * + * @return SequenceInterface */ public function slice(int $offset, ?int $length = null): SequenceInterface { @@ -270,6 +322,8 @@ public function slice(int $offset, ?int $length = null): SequenceInterface /** * {@inheritDoc} + * + * @return SequenceInterface */ public function sort(?callable $comparator = null): SequenceInterface { @@ -294,8 +348,12 @@ public function sum(): float|int /** * {@inheritDoc} + * + * @param TValue ...$values + * + * @return SequenceInterface */ - public function unshift(...$values): SequenceInterface + public function unshift(mixed ...$values): SequenceInterface { if ($values !== []) { array_unshift($this->internal, ...$values); @@ -308,7 +366,7 @@ public function unshift(...$values): SequenceInterface } /** - * @return Generator + * @return Generator */ #[ReturnTypeWillChange] public function getIterator() @@ -320,9 +378,11 @@ public function getIterator() /** * {@inheritDoc} + * + * @param TValue $value */ #[ReturnTypeWillChange] - public function offsetSet($offset, $value): void + public function offsetSet($offset, mixed $value): void { if ($offset === null) { $this->push($value); @@ -333,6 +393,8 @@ public function offsetSet($offset, $value): void /** * {@inheritDoc} + * + * @return TValue */ #[ReturnTypeWillChange] public function &offsetGet($offset) diff --git a/src/Altair/Structure/Vector.php b/src/Altair/Structure/Vector.php index 62b81904..1042d0e4 100644 --- a/src/Altair/Structure/Vector.php +++ b/src/Altair/Structure/Vector.php @@ -29,16 +29,22 @@ * * @link https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd#.gl62k1xqr * + * @template TValue + * + * @implements VectorInterface + * @implements IteratorAggregate + * @implements ArrayAccess */ class Vector implements IteratorAggregate, ArrayAccess, VectorInterface, CapacityInterface { + /** @use SequenceTrait */ use SequenceTrait; use CapacityTrait; /** * Creates an instance using the values of an array or Traversable object. * - * @param array|Traversable|Contracts\CollectionInterface|null $values + * @param array|Traversable|Contracts\CollectionInterface|null $values */ public function __construct($values = null) {