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

Phpamo Laravel Package

willwashburn/phpamo

phpamo is a PHP client for the amoCRM API, offering a fluent, object-oriented wrapper around amoCRM's REST endpoints. It handles authentication, leads, contacts, and custom fields, making it simple to pull amoCRM data into Laravel or plain PHP apps.

Deep Wiki
Context7

Getting Started

willwashburn/phpamo is a very small utility: it builds signed Camo proxy URLs. It does not proxy or fetch images itself — you still need a Camo deployment (self-hosted, Heroku, or an internal service) configured with the same shared secret.

Install it alongside your app:

composer require willwashburn/phpamo

The first place to look is the package’s single source class under src/ and the README example. The entire public surface is effectively a constructor plus one URL-building method, so there is very little to learn.

Minimal use:

use Phpamo\Phpamo;

$camo = new Phpamo(
    'your-shared-secret',              // must match the Camo server's CAMO_KEY
    'https://camo.example.com'         // your Camo endpoint
);

echo $camo->url('http://cdn.example.com/photo.jpg');
// https://camo.example.com/<hmac-sha1-hex>/687474703a2f2f...

The first real use case is mixed-content cleanup: take user-supplied or third-party http:// image URLs and emit an https:// Camo URL in a Blade view, Markdown renderer, RSS feed, or email template.

Implementation Patterns

Bind it once in the container. Treat the secret and Camo host as configuration, not literals:

// config/services.php
'camo' => [
    'key'  => env('CAMO_KEY'),
    'host' => env('CAMO_HOST', 'https://camo.example.com'),
],
// AppServiceProvider::register()
$this->app->singleton(Phpamo::class, fn () => new Phpamo(
    config('services.camo.key'),
    config('services.camo.host'),
));

Wrap it in a small app-level helper. Avoid sprinkling the vendor class through views; a CamoUrl service gives you one place to change policy later:

class CamoUrl
{
    public function __construct(private Phpamo $phpamo) {}

    public function for(?string $url): ?string
    {
        if (! $url || ! str_starts_with($url, 'http')) {
            return $url; // relative paths, data URIs, already handled
        }

        if (str_starts_with($url, 'https://')) {
            return $url; // usually no need to proxy HTTPS sources
        }

        return $this->phpamo->url($url);
    }
}

Use it at render time, not in the database. Generate Camo URLs when composing the response. Signing is a cheap HMAC, so there is no meaningful performance reason to persist derived URLs.

Batch-map content models. For a gallery or feed, map over image fields in a presenter/resource:

$images = $post->images->map(fn ($image) => $camo->url($image->source_url));

Post-process rendered HTML/Markdown when you cannot control the source template. Run the HTML through a DOM pass and rewrite only img[src] values that start with http://. This is a good fit for user-generated Markdown or imported content.

Generate feed and email variants. RSS readers and email clients are the classic mixed-content environments. Emit Camo URLs specifically in those render paths while leaving your main site’s URLs untouched.

Handle multi-tenant secrets at runtime. If each tenant has its own Camo deployment, construct a Phpamo instance per tenant from a repository rather than relying on a single container singleton.

Test the signature, not the string. In a unit test, assert the path segments directly:

$url = 'http://example.com/a.jpg';
$expected = hash_hmac('sha1', $url, 'secret');
$this->assertStringContainsString($expected, $phpamo->url($url));
$this->assertStringEndsWith(bin2hex($url), $phpamo->url($url));

Gotchas and Tips

  • Sign the exact source string. Camo recomputes the HMAC over the URL it receives after hex-decoding. Any normalization you do after signing — adding a query string, changing encoding, trimming — breaks the signature. Decide the canonical URL first, then sign once.
  • Don’t URL-encode the signed value. The final path segment is hex (bin2hex), not percent-encoded. Double-encoding produces a valid-looking URL that Camo rejects.
  • Treat generated URLs as ephemeral. Rotating the Camo shared secret invalidates every previously generated URL. Never store Camo URLs as permanent image locations; store the original source and generate on demand.
  • A Camo URL is not an image guarantee. The package only signs; your Camo server still applies content-type allowlists, size limits, and source filtering. A correctly signed URL can still yield a 403/404 from Camo — check Camo logs, not just PHP.
  • Debug mismatches by recomputing manually. Compare hash_hmac('sha1', $sourceUrl, $secret) and bin2hex($sourceUrl) against the two path segments. Nine times out of ten the mismatch is invisible whitespace, an altered query string, or a different secret between app and proxy.
  • Skip relative paths, data: URIs, and non-HTTP schemes. They cannot be proxied. Also skip https:// sources unless you specifically want Camo’s caching/IP-hiding behavior.
  • Fragments are client-side only. http://x/img.png#foo signs the fragment, but the browser never sends it; Camo’s view of the URL may differ depending on how it parses. Strip fragments before signing.
  • Beware private/internal hosts. Camo fetching http://internal-service/... is an SSRF surface. Enforce an allowlist or block private ranges at the Camo layer; the PHP package will happily sign anything.
  • Watch PHP version compatibility. The last release was April 2020. It is a tiny, dependency-free library, so it usually works on modern PHP, but verify before adopting; if it breaks, reimplementing it is roughly ten lines of hash_hmac and bin2hex.
  • Extension points are minimal by design. If you need retries, alternate signing algorithms, or multiple Camo hosts, wrap the class in your own service rather than forking. The package’s value is the Camo URL contract, not a framework of abstractions.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle