docchula/vesta-client
A Laravel/PHP client for Vesta, Chulalongkorn University's single sign-on and identity platform. It wraps Vesta authentication, token exchange, and user profile endpoints so PHP applications can add Vesta login and identity checks with minimal configuration.
Scope note: no prior assessment existed and the only input for this review is the v1.0.3 release note ("Add support for Guzzle 8"). The guidance below is written for a Laravel package that wraps an HTTP API on top of Guzzle — verify the concrete class and config names against the repo's README and
composer.jsonbefore committing to them.
composer.json first, not the README. The one thing v1.0.3 changes is a dependency constraint. Confirm whether guzzlehttp/guzzle is now ^7.0|^8.0 (a widening, safe to adopt) or ^8.0 only (a narrowing, potentially breaking for anything still on Guzzle 7). This single line determines whether the upgrade is routine or a project-wide dependency decision.Illuminate\Support\Facades\Http) pulls in Guzzle, and most Laravel apps and third-party SDKs are pinned to Guzzle 7. Run composer why guzzlehttp/guzzle and composer update --with-dependencies --dry-run before installing v1.0.3.composer require <vendor>/<package>:^1.0.3
php artisan vendor:publish --tag=<package>-config # confirm tag in README
config/<package>.php (base URI, timeout, credentials). These three files tell you almost everything about day-to-day usage.Resolve from the container, never new. The package's client should be bound as a singleton so config and the underlying Guzzle client are shared. Inject it into constructors, or resolve it in a closure if you only need it in one method.
Bind to an interface of your own. Wrap the package client in a thin application-level service and depend on that interface:
// app/Services/BillingGateway.php
interface BillingGateway {
public function findCustomer(string $id): Customer;
}
This is the cheapest insurance against the package's API surface moving, and it makes the Guzzle major version irrelevant to the rest of your codebase.
Keep configuration in config/ + .env. Credentials, base URI, and timeouts belong in the published config file reading from env(), never hardcoded at call sites. Cache-config behaviour (php artisan config:cache) means every new env key needs a deploy-time value — add it to .env.example in the same PR.
Test with HTTP fakes, not live calls. If the package exposes the underlying Guzzle client or accepts one, inject a MockHandler/HandlerStack; otherwise use Http::fake() if it routes through Laravel's client. Guzzle major versions have historically adjusted handler-stack and middleware APIs, so isolate this setup in one test helper class rather than copy-pasting it.
Push slow or failure-prone calls onto the queue. Wrap the gateway call in a job with $tries and backoff(). Retry semantics are better expressed at the job level than via Guzzle middleware, because you get observability through Horizon/Failed Jobs for free. Use Guzzle middleware (retry, logging) only for transport-level transient errors that never deserve a queue round-trip.
Configure timeouts explicitly per environment. The default is usually "no timeout," which turns a slow upstream into exhausted PHP workers. Set connect and total timeouts in config, and lower them for web requests than for queued jobs.
^7.0. If ^8.0 requires minimum-stability: dev or a pre-release tag, treat v1.0.3 as pre-release and do not ship it to production without pinning.guzzlehttp/guzzle ... conflicts with .... Diagnose with composer why-not guzzlehttp/guzzle 8.* rather than blindly bumping constraints across your dependency tree.^7.0|^8.0) is a non-event. A hard ^8.0 means every consumer must migrate simultaneously; check whether the package released this as a minor (1.0.3) but effectively shipped a breaking dependency change. Semver-wise that's a red flag worth raising upstream.catch blocks: GuzzleHttp\Exception\ConnectException, RequestException, and BadResponseException hierarchies, plus anything calling getHandlerContext(). Prefer catching a package-defined exception or PSR-18 ClientExceptionInterface to avoid coupling to Guzzle's namespace.http_errors. Whether a 4xx/5xx throws or returns a response is a Guzzle request option, and defaults have tripped people up across versions. Assert on a 404 in a test so the behavior is pinned regardless of the version Composer resolves.Macroable trait. If the client is final with a private constructor and no injection seam, treat it as untestable and wrap it at the interface boundary as above.debug option in local only (it writes to stdout and leaks headers), and for production capture status code, request URI, and a truncated body in your exception handler — never the Authorization header.composer.lock and read the next patch note. A dependency-support bump landing in a patch release signals the maintainers treat constraints as non-breaking; watch the following releases for repeat occurrences before relying on semver here.How can I help you explore Laravel packages today?