pugx/godfather
PUGX Godfather is a lightweight PHP library that generates stub classes implementing any interface at runtime, so you can quickly create test doubles and fake dependencies without boilerplate or a full mocking framework. Ideal for unit testing legacy code.
pugx/godfather is a small strategy-pattern registry: implementations are registered under aliases, and callers resolve the implementation they need at runtime instead of writing switch/if chains. It is a 2014-era Symfony2/PHP library, so treat it as legacy and do a short compatibility spike before adopting it.
Minimal steps:
composer require pugx/godfather
app/AppKernel.php:
new PUGX\GodfatherBundle\PUGXGodfatherBundle(),
README.md for the exact registry API and tag nameGodfather.php / the core registry class for get() vs getStrategy() and default-strategy behaviorResources/config/*.xml for the service ID you will injectuse PUGX\Godfather\Godfather;
$godfather = new Godfather();
$godfather->addStrategy('flat_rate', new FlatRateStrategy());
$godfather->addStrategy('carrier', new CarrierStrategy());
$strategy = $godfather->get('flat_rate');
Confirm the exact method names in the package source; older releases vary between get(), getStrategy(), and similar variants.First use case: replace a shipping-cost or payment-method switch with a strategy interface. Define the contract, create one class per algorithm, tag each service with an alias, inject the registry into the service that chooses the algorithm, and resolve by alias.
interface ShippingCostStrategy
{
public function calculate(Order $order): int;
}
final class FlatRateStrategy implements ShippingCostStrategy
{
public function calculate(Order $order): int
{
return 500;
}
}
services:
app.shipping.flat_rate:
class: App\Shipping\FlatRateStrategy
tags:
- { name: godfather.strategy, alias: flat_rate }
app.shipping.carrier:
class: App\Shipping\CarrierStrategy
tags:
- { name: godfather.strategy, alias: carrier }
final class ShippingCostCalculator
{
public function __construct(private Godfather $godfather)
{
}
public function quote(Order $order): int
{
$strategy = $this->godfather->get($order->getShippingMethod());
return $strategy->calculate($order);
}
}
Verify the tag name in the compiler pass before wiring production services; it may be godfather.strategy, pugx.godfather.strategy, or a bundle-specific variant.
Use one registry per decision family. A shipping registry should not contain payment strategies. If the package supports only a single global registry, either keep aliases namespaced (shipping.flat_rate, payment.stripe) or wrap the registry in your own small application-level factory.
Define a narrow interface per strategy family. The registry returns object or a shared interface; the caller should type-check or assert the expected interface when resolving from a broad registry. Day-to-day code should look like this:
$strategy = $this->godfather->get('shipping.flat_rate');
if (!$strategy instanceof ShippingCostStrategy) {
throw new \RuntimeException('Invalid shipping strategy');
}
Keep aliases as application contracts. Prefer constants or a dedicated enum-like class over raw strings scattered through templates, forms, and services:
final class ShippingStrategies
{
public const FLAT_RATE = 'flat_rate';
public const CARRIER = 'carrier';
}
Pass context objects rather than long argument lists. Strategy methods age better when they accept a request/context object:
interface TaxStrategy
{
public function calculate(TaxContext $context): TaxResult;
}
Use the registry at the application boundary, not deep in domain entities. Good integration points are controllers, command handlers, form handlers, and application services. Bad integration points are entities, value objects, and low-level domain services that should not know about the DI container.
Workflow for adding a strategy:
Testing pattern: unit-test each strategy in isolation; test the consumer with a stub registry or a real registry populated manually in standalone mode. Add one integration test that boots the Symfony container and resolves every configured alias to catch tag/compiler-pass mistakes.
If the package supports annotation-based registration, use it only if your team already uses annotations heavily. Explicit service tags are easier to grep and review in legacy Symfony2 projects.
This package is effectively abandoned: last release was 2014. Expect PHP 5.x/Symfony2 assumptions. On PHP 8 or Symfony 5/6, it may fail because of reserved words, container API changes, public/private service defaults, or removed bundle conventions. For new code, prefer Symfony’s native tagged services with !tagged_iterator or a ServiceLocator; use Godfather only when maintaining an existing legacy application.
The service tag name and tag attributes are the most common integration failure. Confirm the exact tag in the package’s compiler pass and inspect the compiled container when a strategy is missing:
php app/console debug:container --tag=godfather.strategy
php app/console cache:clear
In modern Symfony the command is bin/console, but this package predates that layout.
Alias collisions are dangerous. If two services are tagged with the same alias, behavior depends on compiler-pass ordering and may silently overwrite one another. Validate aliases at compile time in your own compiler pass, or at minimum add a container smoke test that resolves every alias.
Unknown aliases often surface as a generic exception from the registry. Wrap resolution at the application boundary and throw a domain-specific exception with the requested alias and the list of known aliases. This is much easier to debug than a bare “strategy not found” from deep in a service.
Avoid turning the registry into a service locator. Injecting Godfather into every class and calling get() from anywhere hides dependencies. Prefer injecting a small application-specific selector that owns the registry and exposes only the operations your application needs.
Watch service visibility and lazy loading. Old Symfony2 code generally assumed public services, while newer Symfony versions default to private services and may remove tagged services that are not referenced. If strategies have heavy constructor dependencies, consider lazy services or keep construction cheap.
Clear the cache after changing tags, aliases, or compiler passes. Symfony compiles the tagged-service map into the cached container, so stale cache is a frequent cause of “my new strategy is not there.”
Use the package’s extension points sparingly. A custom compiler pass can validate aliases, enforce interface implementation, normalize alias names, and inject a default strategy. A decorator around the registry can add logging, metrics, or fallback behavior without modifying the package.
Document the pattern locally. The name “Godfather” does not communicate “strategy registry” to new developers; add a short README section showing the interface, tag format, alias list, and one example call site.
How can I help you explore Laravel packages today?