kzykhys/git
kzykhys/git is a lightweight PHP wrapper around the Git command-line tool, offering an object-oriented API for common repository tasks such as cloning, committing, branching, status, log, and diff. It requires Git installed but no PHP extension, making Git automation simple in PHP projects.
Architecture fit. PHPGit is a thin, object-oriented facade over the git CLI binary — not a libgit2 binding. That means it inherits whatever git version is installed on the host and shells out per operation. For a Laravel application this is an impedance mismatch on three axes: (1) it predates the Laravel service container idiom, so there is no GitServiceProvider, config publishing, or facade to hook into — a TPM must budget for that glue; (2) it holds no awareness of Laravel's filesystem abstraction (Storage::disk()), so paths are raw host paths; (3) at PHP 5.3 vintage it is written against pre-PSR-4 conventions and Symfony 2.x-era components, which is the era Laravel itself abandoned after 5.x. The realistic role for this package is reference implementation or narrow utility inside a controlled worker process — not a first-class application dependency.
Integration feasibility. composer require kzykhys/git will resolve on modern PHP only if the transitive constraints permit it, and that is the single largest unknown. The package was last released 2014-02-15 and its dependency set is Symfony 2.x-era (symfony/process, symfony/filesystem). Laravel 10/11 pin symfony/process at ^6/^7, so a hard conflict is the expected outcome. Workarounds — --ignore-platform-reqs, Composer replace, or a forked composer.json — all convert a dependency-management problem into an owned-code problem. Additionally, PHP 8.x removed several constructs this codebase plausibly relies on (each(), create_function(), and, at 8.2, dynamic-property and ${} interpolation deprecations). A one-day compatibility spike against the target PHP/Laravel matrix is the correct first gate, and it should be expected to fail.
Technical risk.
chdir() to select a repository (common in this era) rather than git -C <path>, it mutates process-wide state. Under Octane/Swoole or a multi-job queue worker, concurrent jobs will operate on the wrong repository — a silent, data-corrupting failure mode.git log, git diff, and git clone on large repositories can produce output that gets buffered into memory; without a streaming or --max-count discipline this becomes an OOM path.Key questions for the spike. (1) What are the exact composer.json constraints on symfony/process and symfony/filesystem, and do they conflict with the target Laravel version? (2) Does it use chdir() or -C/--work-tree? (3) Are all CLI arguments escaped (e.g. via escapeshellarg/Process array form), or interpolated into a string? (4) Is the upstream repository archived or merely dormant, and under what license (MIT?) — confirm redistribution rights for a vendor fork. (5) Does it invoke git with --no-pager, GIT_TERMINAL_PROMPT=0, and a bounded timeout, or can it hang on credential prompts in a web request? (6) Are there maintained alternatives that meet the same need — gitonomy/gitlib (pure PHP), php-git2 (libgit2), or a hand-rolled Symfony Process wrapper over a fixed command allowlist?
Stack fit. Treat PHPGit as a capability reference, not a deployment target. The functionality it exposes (init, clone, add, commit, branch, checkout, log, status) is roughly 150 lines of well-escaped Symfony\Component\Process calls when restricted to a fixed allowlist of subcommands. Re-implementing against the Symfony Process version already present in the framework eliminates the dependency conflict entirely and gives you array-form argument passing (no shell, no injection). If you adopt rather than reimplement, bind it behind your own GitRepository contract so the vendor class never appears in application code.
Migration path.
composer require kzykhys/git, record the exact resolution result or conflict. Run a smoke script through init → add → commit → log → branch.composer.json with corrected constraints and PHP 8 support, publish to a private VCS repo.App\Contracts\GitRepository with the narrow operations you actually need (likely: clone, pull, commit, tag, log, show). Implement CliGitRepository (own code) and, if forked, PhpGitRepository. The contract is the hedge against a second migration.GitServiceProvider with a config file for binary path, working-directory root, timeout, and allowed subcommands. Never hardcode git in PATH — resolve it via config so container images stay portable.Compatibility. Assume nothing about PSR-4: the package is PSR-0-era, which Composer still supports but which behaves differently under --classmap-authoritative and --optimize-autoloader. If you fork, convert to PSR-4 and add a php constraint plus a CI matrix (8.1/8.2/8.3 × Laravel 10/11) so drift is caught. Pin the fork to an exact tag; do not allow ^ ranges on owned forks.
Sequencing. Contract → implementation → container binding → feature flag → shadow-run in a non-production environment against a throwaway repository → enable for one non-critical workflow → widen. Do not sequence this as "adopt package, then refactor" — the package is old enough that refactoring away from it is the default outcome, and building the interface first makes that refactor free.
Maintenance. There is no upstream maintenance to rely on. Ownership burden: a vendor fork is realistically 1–2 engineer-weeks for the initial PHP 8 / Composer remediation plus a security review of argument handling, then a low but non-zero ongoing cost (rebase on git releases, re-verify on each PHP minor). Reimplementation is comparable upfront and materially cheaper thereafter. Licensing is likely permissive (MIT-style) — verify before vendoring, and record it in the SBOM either way.
Support. No security advisories will ever be issued for this package, which cuts both ways: no patch treadmill, but also no disclosure channel. Your team becomes the CVE response function. If the tool touches tenant-controlled remotes or repository contents, add it to the threat model explicitly and require an audit of every shell invocation.
Scaling. Each call forks a process and touches disk — this is IO- and CPU-bound work that must never run on a web request thread. Route all operations to dedicated queue workers with a per-job working directory (storage/app/git/{uuid}), sized concurrency, and a Horizon supervisor separate from latency-sensitive queues. Expect: process spawn overhead in the tens of milliseconds, full-clone costs proportional to repo size, and disk growth that needs a reaper/TTL policy on working directories. Clones are the dominant cost; prefer --depth 1 --filter=blob:none and cache mirrors per remote where possible.
Failure modes. Enumerate these in the runbook before launch:
index.lock contention — concurrent jobs against the same working directory; solved by strict per-job directory isolation.git (credential prompt, network stall) occupies a worker slot indefinitely; enforce Process timeouts and GIT_TERMINAL_PROMPT=0.chdir() is in play, concurrent request handling is unsafe; either forbid this package under Octane or verify -C usage.Ramp-up. The object API is genuinely pleasant and the learning curve for using it is under a day — that is the package's real residual value and the reason it earned 176 stars. The cost sits elsewhere: the team must independently own git plumbing semantics (detached HEAD, merge conflicts, ref vs. working-tree state) and safe concurrency patterns. Budget for a written internal standard covering allowed subcommands, argument escaping, working-directory isolation, and timeout policy. Without that standard, the package's convenience will outrun the team's containment discipline.
Bottom line for the TPM: the package is a useful design reference but a poor dependency — stale by roughly a decade, with a likely framework conflict and an unverified command-injection surface. Fund a one-to-two-day spike, default to a contract-first reimplementation over Symfony Process, and only adopt the fork if the spike clears both the Composer resolution and the escaping audit cleanly.
How can I help you explore Laravel packages today?