ptachoire/tac
TAC (Tiny Active Record) is a lightweight, Composer-installable PHP ORM by ptachoire. It maps database tables to PHP objects through a simple ActiveRecord API, providing CRUD operations and basic query helpers with minimal configuration—a compact alternative to heavier ORMs for small projects.
Verdict up front: as a TPM, treat this as a capability to scope, not a dependency to adopt. ptachoire/tac is a tiny, MIT-licensed PHP utility providing Unix-style tac (reverse line order) and tail (last N lines) file reading without shelling out to native binaries. It is interesting only as a reference implementation for a small diagnostic feature. With 1 star, 0 dependents, no observable release cadence, and no downstream integration signal, the supply-chain and ownership cost exceeds the cost of writing an equivalent internal helper.
Architecture fit. Single-responsibility, framework-agnostic, and PSR-4 autoloaded — the right shape from an architectural standpoint. There is no evidence of service providers, config publishing, or container bindings, which means it can sit behind a thin internal port (LogReader / FileTailer) without dragging Laravel conventions into domain code. The risk is the mirror image of over-coupling: it is under-specified. We cannot confirm from the metadata whether it operates on file paths, SplFileObject instances, or raw resources, whether it returns arrays or generators, or whether it supports a follow/-f mode. Those unknowns determine whether it is usable at all for our actual scenarios.
Integration feasibility. Mechanically trivial (composer require ptachoire/tac) but procedurally expensive:
Tac\ namespace mapping, and that the version constraint resolves under PHP 8.2/8.3. Code of this vintage frequently pins php: >=5.3.each(), implicit resource handling, and untyped iteration that now warns or fatals.Technical risk. Dominant risks are algorithmic, not structural:
\n alone break on CRLF and multibyte content; the last line of a file with no trailing newline is a classic off-by-one.Key questions before any build decision.
tail expose follow mode, and if so does it block (unacceptable inside a request cycle)?Stack fit. Laravel already supplies most of the surrounding ergonomics — Storage::disk()->path(), File::lines(), SplFileObject, and StreamedResponse for chunked output. The only genuinely missing primitive is reverse iteration. That is the sole piece worth borrowing. Introducing a global dependency for it is not justified. Recommendation: vendor the algorithm, not the package — port the logic into app/Support/File/ReverseLineReader.php behind an interface, with the MIT attribution preserved in the file header.
Migration path (if a dependency is mandated by stakeholders).
internal/tac) rather than consuming ptachoire/tac in composer.json. Pin to a commit SHA, never a branch.App\Contracts\ReverseLineReader with readBackwards(string $path, int $limit): Generator and tail(string $path, int $lines): array. No call site may reference the vendor namespace.Compatibility. Target PHP 8.2+ with strict types. Reverse reads must return a Generator to keep memory flat. Non-seekable input must raise a domain exception, not a generic one, so callers can explicitly degrade to a buffered strategy or refuse the request.
Sequencing.
| Step | Action | Gate |
|---|---|---|
| 1 | Source read-through and license check | API surface documented in the ticket |
| 2 | Spike branch: port to internal namespace on PHP 8.2 | PHPStan clean, Rector applied |
| 3 | Benchmark vs. tac CLI and SplFileObject on 100 MB / 5 GB fixtures |
Memory flat, throughput acceptable |
| 4 | Build vs. fork decision | In-house ~80-line version preferred unless the port reveals non-obvious edge cases |
| 5 | Contract tests and interface landing | ≥ 95% coverage on the reader |
| 6 | Wire one real consumer behind a feature flag | No vendor namespace outside Support |
Step 2 is the decision point: for a problem domain this small, if the source is under 100 lines it collapses into a straight rewrite and the package is discarded. Budget one engineer-day across steps 1–5.
Maintenance. Effectively zero upstream. One star, zero dependents, no release history — any adoption is a permanent fork with us as sole maintainer. That is acceptable only because the algorithm is small and static; reverse line reading has not meaningfully changed in decades. It is not acceptable to carry the original package name in composer.json, because a future composer update will either fail opaquely or silently pull an unreviewed commit.
Support. No SLA, no triage, no security-response channel. Mitigation: assign ownership to the platform team, preserve the license header, and leave a README in the vendored file pointing at upstream URL and commit SHA for future archaeology. If a security review process exists, record this as an accepted risk with a documented rationale rather than an unexamined pass.
Scaling. This is a per-invocation local-filesystem utility, not distributed infrastructure. Two constraints dominate:
tac. Appropriate for config files, small exports, and bounded log slices; not appropriate for tailing application logs under load. If the real requirement is "show the last 200 lines of the error log," the correct architecture is a log aggregator or a StreamedResponse piped from tail -n, not a PHP package reading disk on every request.Failure modes to instrument.
OutOfMemoryError or request timeout on large files if the implementation buffers rather than seeks — this is the acceptance test, not a footnote.stat and fopen) — open the handle once, never re-resolve the path mid-read.follow mode hanging a PHP-FPM worker — explicitly disable or refuse to expose it.Ramp-up. Negligible for engineering. The conceptual surface is one screen of code; any mid-level PHP developer can read, port, and test it in half a day. The real cost is organizational: convincing reviewers that a one-star dependency is a liability rather than a shortcut, and documenting why we vendored instead of required. Total budget one engineer-day including the build-vs-fork analysis and contract tests — an order of magnitude cheaper than onboarding, pinning, and periodically re-auditing an unmaintained third-party package for a capability we can own outright.
How can I help you explore Laravel packages today?