hyperf/contract
Core contracts for Hyperf: a set of lightweight PHP interfaces that define common behaviors across the framework (DI, events, middleware, serialization, etc.). Helps decouple components, improve testability, and keep implementations swappable.
Installation Require the package via Composer:
composer require hyperf/contract
No configuration is needed—it provides only interfaces.
First Use Case
Define a service class implementing a Hyperf contract, such as Hyperf\Contract\CacheInterface:
use Hyperf\Contract\CacheInterface;
class RedisCache implements CacheInterface
{
public function get($key, $default = null)
{
// Redis logic
}
public function set($key, $value, $ttl = null)
{
// Redis logic
}
}
Where to Look First
src/Contract for available interfaces.Hyperf\Contract\ namespace to discover relevant interfaces.Leverage Hyperf’s DI container to bind contracts to implementations:
// In a service provider or config file
$container->bind(
Hyperf\Contract\CacheInterface::class,
\App\Cache\RedisCache::class
);
@Inject or @Bind in classes for automatic resolution:
use Hyperf\Di\Annotation\Inject;
class OrderService
{
#[Inject]
public CacheInterface $cache;
}
Implement lifecycle hooks using contracts like OnRequest:
use Hyperf\Contract\OnRequest;
use Psr\Http\Message\ResponseInterface;
class AuthMiddleware implements OnRequest
{
public function process($request, Closure $next): ResponseInterface
{
if (!$request->hasHeader('Authorization')) {
return new Response(401);
}
return $next($request);
}
}
Register middleware in config/autoload/middlewares.php:
return [
'http' => [
AuthMiddleware::class,
],
];
Define modular boundaries with contracts (e.g., QueueWorkerInterface):
use Hyperf\Contract\QueueWorkerInterface;
class PaymentProcessor implements QueueWorkerInterface
{
public function handle($job, $data)
{
// Process payment
}
}
Register workers in config/autoload/queues.php:
return [
'default' => [
'worker' => PaymentProcessor::class,
],
];
Use contracts for logging, caching, or auth:
use Hyperf\Contract\LoggerInterface;
class OrderService
{
public function __construct(private LoggerInterface $logger)
{
}
public function create()
{
$this->logger->info('Order created');
}
}
Mock contracts in unit tests:
use Hyperf\Contract\CacheInterface;
use PHPUnit\Framework\TestCase;
class OrderServiceTest extends TestCase
{
public function testCache()
{
$cache = $this->createMock(CacheInterface::class);
$cache->method('get')->willReturn('cached_value');
$service = new OrderService($cache);
$this->assertEquals('cached_value', $service->getCachedData());
}
}
Hyperf-Specific Assumptions
Hyperf\Contract\ContextInterface). Avoid using these in non-Hyperf contexts.Psr\Cache\CacheItemPoolInterface).DI Container Conflicts
$container->bind(
Hyperf\Contract\CacheInterface::class,
function ($container) {
return new LaravelCacheAdapter($container->make(\Illuminate\Contracts\Cache\Store::class));
}
);
Missing Implementations
Hyperf\Contract\ConfigInterface requires a concrete Config class).Hyperf\Config\Config).Version Skew
"hyperf/contract": "3.1.*"
Coroutines in Non-Hyperf Code
Hyperf\Contract\CoroutineInterface are Hyperf-specific. Avoid in Laravel.Generator or Amp for async tasks.Contract Not Resolved?
php bin/hyperf container:list
Middleware Not Triggering
middlewares.php and that the class implements OnRequest/OnException.Queue Workers Ignored
queues.php and the class implements QueueWorkerInterface.IDE Not Recognizing Contracts
phpstorm.meta.php:
namespace Hyperf\Contract {
interface CacheInterface {}
// ... other interfaces
}
Custom Contracts Extend Hyperf’s contracts for domain-specific needs:
namespace App\Contracts;
use Hyperf\Contract\CacheInterface;
interface DomainCacheInterface extends CacheInterface
{
public function getDomainData(string $domain);
}
Adapter Pattern Create Laravel-compatible adapters for Hyperf contracts:
class LaravelCacheAdapter implements Hyperf\Contract\CacheInterface
{
public function __construct(private \Illuminate\Contracts\Cache\Store $cache) {}
public function get($key, $default = null)
{
return $this->cache->get($key, $default);
}
}
Dynamic Bindings Use closures for runtime bindings:
$container->bind(
Hyperf\Contract\LoggerInterface::class,
fn() => new MonologLogger()
);
Testing Helpers Create a base test case with pre-configured mocks:
abstract class HyperfTestCase extends TestCase
{
protected function mockCache(): CacheInterface
{
return $this->createMock(CacheInterface::class);
}
}
$container->bind(
Hyperf\Contract\CacheInterface::class,
fn() => new RedisCache()
);
Autoloading
Ensure composer dump-autoload is run after adding new contracts.
Namespace Conflicts
Avoid naming collisions (e.g., App\Contracts\CacheInterface vs. Hyperf\Contract\CacheInterface). Use distinct namespaces.
Hyperf-Specific Config
Some contracts rely on Hyperf’s config system (e.g., Hyperf\Contract\ConfigInterface). Mock these in tests:
$config = $this->createMock(ConfigInterface::class);
$config->method('get')->willReturnMap([
['cache.driver', 'redis'],
]);
How can I help you explore Laravel packages today?