Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Curl Laravel Package

yakeing/php_curl

A simple PHP cURL wrapper for making HTTP requests without low-level cURL boilerplate. Supports GET/POST, custom headers, cookies, proxies, and file uploads, helping you fetch remote content or call APIs quickly from any PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. yakeing/php_curl is a small, single-purpose cURL wrapper: 36 stars, last tagged release August 2019, and the repository is archived. MPL-2.0 licensed. There is no service provider, no facade, no config file, and no Laravel integration of any kind — it is a plain PHP class you instantiate yourself. Treat it as a "thin convenience layer over ext-curl", not as an HTTP client you build a codebase around.

Step 1 — read the source before installing. The package is small enough to audit in ten minutes. Open src/ on GitHub and note three things:

  • the exact namespace and class name (commonly something like Yakeing\Curl\Curl),
  • the public method surface (GET/POST helpers, cookie setter, form/file upload, auth options),
  • how it signals failure — a false/empty-string return, a thrown \Exception, or a curl_error() string. This single detail determines every wrapper you write around it.

Step 2 — install and confirm it loads.

composer require yakeing/php_curl
php -r 'require "vendor/autoload.php"; var_dump(class_exists("Yakeing\\Curl\\Curl"));'

If Composer resolves nothing, check the exact Packagist name against the name field in the repo's composer.json — small packages like this are frequently renamed or unpublished, and a 2019 package may declare constraints (php: >=5.6) that are merely stale rather than broken.

Step 3 — first use case. The one thing this package is genuinely convenient for is a stateful cookie-driven flow against a legacy endpoint: log in, keep the session, submit a form, upload a file. That is the demo to run first:

$curl = new \Yakeing\Curl\Curl();
$curl->setCookieFile(storage_path('app/cookies/session.txt'));

$curl->post('https://legacy.example.com/login', [
    'user' => config('services.legacy.user'),
    'pass' => config('services.legacy.pass'),
]);

$html = $curl->get('https://legacy.example.com/report');

Step 4 — make the build-vs-buy call now, not later. For anything greenfield, Illuminate\Support\Facades\Http (Guzzle under the hood) already covers cookies (->withCookies()), multipart (->attach()), basic/digest auth (->withBasicAuth(), ->withDigestAuth()), retries, timeouts, and Http::fake() testing. Adopt this package only if (a) you are maintaining an existing integration that already depends on it, or (b) you specifically want its cookie-jar or form-building ergonomics on a PHP project with no Laravel HTTP layer. Score 0.15 and an archived repo are a fair summary of the risk.

Implementation Patterns

Never call it directly from controllers, jobs, or commands. Isolate it behind one interface so the eventual migration off an archived dependency is a one-file change.

namespace App\Support\Http;

interface LegacyClient
{
    public function get(string $path, array $query = []): string;
    public function post(string $path, array $data = []): string;
    public function upload(string $path, string $field, string $localFile): string;
}
namespace App\Support\Http;

use Yakeing\Curl\Curl;

class YakeingLegacyClient implements LegacyClient
{
    public function __construct(private Curl $curl, private string $baseUrl) {}

    public function get(string $path, array $query = []): string
    {
        return $this->guard(fn () => $this->curl->get($this->url($path, $query)));
    }

    public function post(string $path, array $data = []): string
    {
        return $this->guard(fn () => $this->curl->post($this->url($path), $data));
    }

    public function upload(string $path, string $field, string $localFile): string
    {
        return $this->guard(fn () => $this->curl->post(
            $this->url($path),
            [$field => new \CURLFile($localFile)]
        ));
    }

    private function url(string $path, array $query = []): string
    {
        $url = rtrim($this->baseUrl, '/') . '/' . ltrim($path, '/');
        return $query ? $url . '?' . http_build_query($query) : $url;
    }

    private function guard(callable $call): string
    {
        try {
            $body = $call();
        } catch (\Throwable $e) {
            throw new LegacyTransportException($e->getMessage(), 0, $e);
        }

        if (! is_string($body) || $body === '') {
            throw new LegacyTransportException('Empty or non-string response body.');
        }

        return $body;
    }
}

Bind it once, in a dedicated provider.

// app/Providers/LegacyHttpServiceProvider.php
public function register(): void
{
    $this->app->singleton(LegacyClient::class, fn () => new YakeingLegacyClient(
        new \Yakeing\Curl\Curl(),
        config('services.legacy.base_url'),
    ));
}

Keep base_url, credentials, and the cookie-jar path in config/services.php / .env; never inline them. Note the singleton caveat below.

Always set timeouts and verify TLS. If the wrapper exposes no timeout setter, reach for the underlying handle if it exposes one, or wrap the call in a job that enforces duration. Do not accept library defaults:

// If the class exposes the handle or option setters, use them:
$curl->setTimeout(10);          // CURLOPT_TIMEOUT
$curl->setConnectTimeout(3);    // CURLOPT_CONNECTTIMEOUT
// And never ship a production config that disables CURLOPT_SSL_VERIFYPEER.

Run slow calls in queues, with the job timeout above the cURL timeout. A blocking cURL request inside a web request is how you get 30-second TTFB and exhausted PHP-FPM workers:

class PushOrderToLegacy extends ShouldQueue
{
    public $timeout = 30;
    public $tries = 3;
    public $backoff = [5, 30, 120];

    public function handle(LegacyClient $client): void
    {
        $client->post('/orders', $this->order->toLegacyPayload());
    }
}

Test at the interface boundary. Http::fake() will not intercept this package — it bypasses Laravel's HTTP layer entirely. Bind a fake LegacyClient in tests instead, and keep a single, small integration test that hits a local stub server for the real wrapper:

$this->app->instance(LegacyClient::class, new FakeLegacyClient(['/report' => '<html/>']));

Migration path. Because everything goes through LegacyClient, swapping in a Guzzle/Http-backed implementation is a new class plus one binding change. Write the interface with the future client in mind (status codes, headers, exceptions) and do not leak the package's return conventions into your domain code.

Gotchas and Tips

  • Archived means unpatched. No releases since 2019, no CVE response, no PHP 8.x guarantees. Pin the version exactly in composer.json, and treat any security issue as yours to fix. If you fork it, MPL-2.0 is file-level copyleft: modifications to its files must be published, but your application code that merely calls it can stay closed. Keep any patched copy in a separate, clearly-identified namespace.
  • No PSR-18 / PSR-7. You get no Guzzle middleware, no retry(), no Laravel Http::pool() concurrency, no Http::fake(). Retries, backoff, and circuit-breaking must be hand-rolled in your wrapper or handled by the queue.
  • Failure semantics are the top debugging trap. Small wrappers like this often return the body as a string and swallow the HTTP status entirely, so a 500 response looks identical to success. Immediately add status/error capture (via the handle if exposed, otherwise by asserting on response content) and log curl_errno() / curl_error() alongside the URL.
  • Singleton + cookie jar = cross-tenant leakage. If you register the client as a singleton and it carries a mutable cookie file, one user's session can bleed into another's request under queues and Octane. Use a scoped binding, or a per-request cookie file path (storage_path('app/cookies/'.Str::uuid().'.txt')) and clean it up in a finally.
  • Cookie file paths must be writable. storage_path() is safe; sys_get_temp_dir() is not always present or writable in containerized/read-only filesystems. Create the directory, and remember Laravel's storage:link does not cover app/ subdirectories automatically.
  • Multipart uploads: use CURLFile. The old '@/path/to/file' string syntax was removed in PHP 5.5+. If the package's form builder still concatenates @ prefixes, uploads will silently send a literal string as the field value — one of the most common silent failures in legacy cURL wrappers.
  • Blocking I/O is blocking I/O. This class does not do async. Fan-out to N endpoints sequentially becomes N × latency. If you need concurrency, this is the wrong tool regardless of the wrapper.
  • Debugging. Turn on verbose cURL output in local/staging only, and capture the request/response pair through Laravel's HTTP client events analogue — a simple Log::debug() in your guard() covering URL, duration, byte length, and error is usually enough. Never log credentials or full cookie contents.
  • Extension points are limited to subclassing and decoration. There is no middleware pipeline. Prefer decoration (the interface above) over extending the class, so your behaviour survives a package swap.
  • Stale composer.json constraints. Old packages sometimes require php: >=5.6 with no upper bound (fine) or, worse, a pin like ~7.0 that breaks installs on PHP 8. Check before you commit; --ignore-platform-reqs is not a fix, it is a postponement.
  • Verdict. Use it only to service an existing integration, wrapped behind an interface, pinned, and with a migration ticket filed the same day. For new work, Http:: / Guzzle gives you the same cookie, form, auth, and upload features with active maintenance, first-class testing support, and no MPL considerations.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr