versionable/prospect
Prospect is a Laravel package for managing sales prospects and leads. It provides models, migrations, and workflow helpers to track contacts, stages, and interactions, making it easy to add a lightweight CRM workflow to your Laravel application.
versionable/prospect is a legacy, pre-PSR HTTP client from the PHP 5.3 era. Before writing a single line of integration code, verify it still installs on your stack — most modern Laravel apps (PHP 8.1+) will not be able to load it.
composer require versionable/prospect in a throwaway Laravel skeleton. If Composer resolves it at all, inspect vendor/versionable/prospect/src and look for the main client class (typically something like Prospect\Client, Prospect\Request, or an Http façade). There is no Laravel service provider, no facade, and no config file — everything is instantiated manually.get, post, send, execute, call). Old HTTP clients usually expose something like:
$client = new Prospect\Client();
$response = $client->get('https://api.example.com/users');
echo $response->getBody();
Confirm the exact method names and the response object shape (does it return a Response object, a raw string, or an array?) before building on top of it.public function index()
{
$client = new \Prospect\Client();
$response = $client->get('https://httpbin.org/get');
return response($response->getBody())->header('Content-Type', 'application/json');
}
https:// endpoint early — if the handshake fails, you'll know before you've built anything around it.Given the package's age and lack of Laravel integration, the right pattern is thin wrapper + dependency injection, so that the client is invisible to the rest of your app and can be replaced by Guzzle in one file if needed.
1. Wrap it in a dedicated client/service class
namespace App\Services;
use Prospect\Client; // verify actual FQCN
class LegacyApiClient
{
private $client;
public function __construct()
{
$this->client = new Client();
}
public function fetchUsers(): array
{
$response = $this->client->get(config('services.legacy.url') . '/users');
return json_decode($response->getBody(), true) ?? [];
}
}
2. Bind it into the container (since the package won't do it for you)
// AppServiceProvider::register()
$this->app->singleton(LegacyApiClient::class, fn () => new LegacyApiClient());
3. Inject it like any other service
public function __construct(private LegacyApiClient $api) {}
public function index()
{
return UserResource::collection($this->api->fetchUsers());
}
4. Queued jobs for anything not on the request's critical path. The client is synchronous and blocking. Wrap calls in a job so slow upstream responses don't stall requests:
class SyncUsers implements ShouldQueue
{
public function handle(LegacyApiClient $api): void
{
foreach ($api->fetchUsers() as $row) {
User::updateOrCreate(['external_id' => $row['id']], $row);
}
}
}
5. Testing. Because the client is constructed internally in the sample above, extract an interface (UserProviderContract) and bind a fake in tests. Alternatively, inject the raw Client via the container so you can swap it:
$this->app->instance(\Prospect\Client::class, $fakeClient);
Confirm the internal client makes any network calls lazily (some 5.3-era clients fire requests on construction) — if it does, the fake must still avoid touching the network.
6. Configuration via config/services.php. No package config exists; put base URLs, timeouts, and credentials there and pass them into the wrapper. Never hardcode them.
7. Rate limiting. Wrap outbound calls in RateLimiter::attempt() in Laravel so a burst from a queue worker doesn't hammer the upstream API — the package has no built-in retry/backoff.
??, no strict types, often no namespaces or missing use statements) will not parse on PHP 8. Even if it parses, deprecated functions (each(), create_function, mysql_*) will fatal. Test in a staging container before adopting.psr-4/psr-0 autoload block, or use a vendor/autoload.php-style include. If composer dump-autoload -o reports class-not-found, you'll have to add a classmap entry in your root composer.json.UserProviderContract, HttpContract) around it.AppServiceProvider.cacert.pem. You may hit SSL certificate problem: unable to get local issuer certificate. Workarounds: point to openssl.cafile in php.ini, or set curl.cainfo. Do not disable verification (CURLOPT_SSL_VERIFYPEER => false) in anything close to production.Spatie\Async or move to Guzzle's Pool/promises.null or a string on failure rather than throwing. Check the response object's status code and body before decoding, and throw yourself — otherwise failures surface as confusing downstream errors (e.g. json_decode(null) returning null).Laravel\Horizon's or Spatie\Backoff around the wrapper, not inside the client.Log::debug() around the call boundary with URL, status, and elapsed duration — Log::withContext in a middleware or job is the only observability you'll get.Http::withOptions([...]) (Guzzle) once, side by side, to prove whether the failure is TLS, DNS, headers, or the upstream itself. It isolates whether the client is at fault.composer audit and pin the exact version; treat it as a liability, not an asset. Do not use it for anything carrying secrets where Guzzle is available.laravel/framework), is PSR-18 compliant, and is supported. Wrap it in the same LegacyApiClient class and no other file changes.How can I help you explore Laravel packages today?