Based on real development experience and issues encountered during implementation.
Nota: as três seções abaixo ("PSR-7 Compatibility Issues", "Header Access Issues", "Express.js Pattern Issues") descreviam uma arquitetura anterior à introdução de suporte PSR-7 nativo neste pacote (
CHANGELOG.md, v0.0.2: "Removed obsolete Psr7CompatibilityAdapter"). O exemplo real e funcional deste pacote (examples/server.php) usaServerRequestInterface/ResponseInterfacePSR-7 diretamente, com handlers que retornamResponseInterface— não o padrãofunction($req, $res): voiddescrito abaixo. Corrigido o que dava para verificar contraexamples/server.php; o restante foi atualizado com uma nota de cautela em vez de reescrito linha a linha.
O script switch-psr7-version.php e o erro de incompatibilidade de getProtocolVersion()
descrito abaixo eram um problema real quando pivotphp/core só suportava PSR-7 v1.x
ou v2.x, exigindo a troca manual. Desde então, pivotphp/core passou a suportar as
duas versões simultaneamente sem precisar do script (ver pivotphp-core, changelog
da versão que corrigiu a compatibilidade real com psr/http-message 2.0). Se você
ainda encontrar esse erro, confira primeiro se está usando uma versão desatualizada
de pivotphp/core antes de recorrer ao script abaixo.
Conteúdo original (pode estar obsoleto)
Error: "Declaration of React\Http\Io\AbstractMessage::getProtocolVersion() must be compatible with..."
Symptoms:
Fatal error: Declaration of React\Http\Io\AbstractMessage::getProtocolVersion()
must be compatible with Psr\Http\Message\MessageInterface::getProtocolVersion(): string
Cause: PivotPHP Core is using PSR-7 v2.x while ReactPHP requires PSR-7 v1.x.
Solution:
# Switch PivotPHP Core to PSR-7 v1.x
php vendor/pivotphp/core/scripts/switch-psr7-version.php 1
composer update psr/http-message
# Verify the change
php vendor/pivotphp/core/scripts/switch-psr7-version.php --checkPrevention: Always use PSR-7 v1.x when integrating with ReactPHP.
Assinatura real (src/Bridge/RequestBridge.php:29):
convertFromReact(ServerRequestInterface $reactRequest): ServerRequestInterface.
Não converte para a classe híbrida PivotPHP\Core\Http\Request — o erro descrito
originalmente nesta seção partia da premissa contrária. Se você receber um erro de
tipo aqui, confira se está passando ServerRequestInterface corretamente pela cadeia
de handlers, não tentando forçar conversão para \PivotPHP\Core\Http\Request.
examples/server.php (o exemplo real e funcional deste pacote) acessa headers via
$request->getHeaders() PSR-7 puro. Não confirmamos, dentro deste repositório, que
->header('camelCase') seja a API real de acesso a headers — se você encontrar esse
método, prefira a forma PSR-7 abaixo, que sabemos que funciona:
// ✅ PSR-7 padrão — confirmado em examples/server.php
$contentType = $request->getHeaderLine('Content-Type');
$auth = $request->getHeaderLine('Authorization');
$allHeaders = $request->getHeaders();Conteúdo original (não verificado nesta correção — pode estar obsoleto)
Symptoms:
$contentType = $request->header('Content-Type'); // Returns nullCause: PivotPHP converts header names to camelCase during Request construction.
Solution: Use camelCase header names (contentType, authorization, xApiKey, etc.)
examples/server.php mostra o padrão real: handlers de rota recebem
ServerRequestInterface (e array $args para parâmetros de rota) e retornam
ResponseInterface, sem um segundo parâmetro de response:
// ✅ Confirmado em examples/server.php
$router->get('/hello/{name}', function (ServerRequestInterface $request, array $args): ResponseInterface {
return Response::json(['message' => "Hello, {$args['name']}!"]);
});Middleware ($app->use(...)), por sua vez, usa um padrão de 3 argumentos que
retorna a resposta (não é void):
// ✅ Confirmado em examples/server.php
$app->use(function (ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface {
$response = $next($request, $response);
return $response->withHeader('X-Server', 'PivotPHP/ReactPHP');
});Conteúdo original (contradiz o exemplo real acima — mantido só como histórico)
Versões anteriores desta página descreviam um padrão function($req, $res): void para
rotas, com a resposta sendo enviada via $response->json($data) em vez de retornada.
Isso não bate com examples/server.php — trate como obsoleto.
Symptoms:
ArgumentCountError: Too few arguments to function PivotPHP\Core\Core\Application::get(),
1 passed and exactly 2 expected
Cause: Using wrong method to access services from container.
Solution: Use make() instead of get():
// ❌ Wrong
$router = $app->get('router');
// ✅ Correct
$router = $app->make('router');
// ✅ With existence check
if ($app->has('router')) {
$router = $app->make('router');
}Prevention: Use PSR-11 container methods: has(), make().
Symptoms: Services registered but not accessible, "Service not found" errors.
Cause: Accessing services before calling boot().
Solution: Always boot before accessing services:
// ✅ Correct order
$app->register(new AppServiceProvider());
$app->register(new RouteServiceProvider());
$app->boot(); // Boot first!
// Now services are available
$config = $app->make('config');
$router = $app->make('router');Prevention: Follow the register → boot → use pattern.
Symptoms:
Error: Class "PivotPHP\Core\Http\Factory\Psr17Factory" not found
Cause: Using incorrect factory class names.
Solution: Use correct PivotPHP factory classes:
// ❌ Wrong - this class doesn't exist
use PivotPHP\Core\Http\Factory\Psr17Factory;
// ✅ Correct factory classes
use PivotPHP\Core\Http\Psr7\Factory\RequestFactory;
use PivotPHP\Core\Http\Psr7\Factory\ResponseFactory;
use PivotPHP\Core\Http\Psr7\Factory\ServerRequestFactory;
use PivotPHP\Core\Http\Psr7\Factory\StreamFactory;
use PivotPHP\Core\Http\Psr7\Factory\UriFactory;Prevention: Check actual class locations in PivotPHP Core.
Symptoms:
Error: Call to undefined method React\EventLoop\Loop::create()
Cause: Using non-existent ReactPHP Loop method.
Solution: Use existing loop instance:
// ❌ Wrong - create() doesn't exist
protected function tearDown(): void
{
$this->loop->stop();
Loop::set(Loop::create()); // This fails
}
// ✅ Correct - just stop the loop
protected function tearDown(): void
{
$this->loop->stop();
parent::tearDown();
}Prevention: Use ReactPHP documentation for correct Loop API.
Symptoms:
Error: Call to undefined method PivotPHP\Core\Http\HeaderRequest::set()
Cause: Trying to modify immutable Request objects.
Solution: PivotPHP Request is immutable by design. Use global variables:
// ❌ Wrong - trying to modify after creation
$pivotRequest->headers->set('content-type', 'application/json');
$pivotRequest->query->page = 2;
// ✅ Correct - set globals before creation
$_SERVER['HTTP_CONTENT_TYPE'] = 'application/json';
$_GET['page'] = '2';
$_POST['data'] = 'value';
$pivotRequest = new Request('POST', '/users', '/users');Prevention: Understand PivotPHP's immutable design pattern.
Symptoms:
RuntimeException: Failed to listen on "tcp://127.0.0.1:8080": Address already in use (EADDRINUSE)
Cause: Port is already in use by another process.
Solutions:
# Find process using the port
lsof -i :8080
netstat -tulpn | grep :8080
# Kill the process
kill -9 <PID>
# Or use a different port
$server->listen('0.0.0.0:8081');Prevention: Use different ports for different environments.
Symptoms:
Error: Typed property PivotPHP\ReactPHP\Server\ReactServer::$socketServer
must not be accessed before initialization
Cause: Accessing ReactServer properties before calling listen().
Solution: Call methods in correct order:
$server = new ReactServer($app, $loop);
$server->listen('0.0.0.0:8080'); // Initialize first
$server->getLoop(); // Now safe to access propertiesPrevention: Always call listen() before accessing server properties.
Symptoms: Memory usage grows continuously over time.
Debugging:
// Add memory monitoring
$app->get('/memory', function ($request, $response) {
$response->json([
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'memory_limit' => ini_get('memory_limit')
]);
});Solutions:
- Avoid static arrays that grow over time
- Clean up resources explicitly
- Use object pooling for frequently created objects
- Monitor memory regularly
// ❌ Wrong - static array grows forever
class MyController {
private static $cache = [];
public function index($request, $response) {
self::$cache[] = $someData; // Memory leak!
}
}
// ✅ Correct - limit cache size
class MyController {
private static $cache = [];
private const MAX_CACHE_SIZE = 1000;
public function index($request, $response) {
if (count(self::$cache) >= self::MAX_CACHE_SIZE) {
array_shift(self::$cache); // Remove oldest
}
self::$cache[] = $someData;
}
}Prevention: Design for long-running processes from the start.
Symptoms: "MySQL server has gone away" errors after period of inactivity.
Solution: Implement connection health checks:
$app->singleton('database', function() {
return new DatabaseConnection();
});
$app->get('/api/users', function ($request, $response) use ($app) {
try {
$db = $app->make('database');
// Health check before query
if (!$db->ping()) {
$db->reconnect();
}
$users = $db->query('SELECT * FROM users');
$response->json($users);
} catch (PDOException $e) {
$response->status(503)->json(['error' => 'Database unavailable']);
}
});Prevention: Use connection pooling or implement automatic reconnection.
Set environment variables:
APP_DEBUG=true
APP_ENV=development
REACTPHP_DEBUG=trueAdd debug middleware:
if ($_ENV['APP_DEBUG'] ?? false) {
$app->use(new DebugMiddleware());
}Add comprehensive logging:
use Psr\Log\LoggerInterface;
$server = new ReactServer($app, $loop, $logger);
// Log all requests
$app->use(function ($request, $response, $next) use ($logger) {
$start = microtime(true);
$next($request, $response);
$duration = microtime(true) - $start;
$logger->info('Request processed', [
'method' => $request->getMethod(),
'path' => $request->getPath(),
'duration' => $duration,
'memory' => memory_get_usage(true)
]);
});# PHPStan static analysis
composer phpstan
# PSR-12 code style check
composer cs:check
# Fix code style issues
composer cs:fix
# Run all tests
composer test
# Run all quality checks
composer quality:checkExample GitHub Actions workflow:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
- run: composer install
- run: composer quality:check
- run: composer testWhen reporting issues, include:
- PHP Version:
php --version - PivotPHP Version: Check
composer.json - ReactPHP Version: Check
composer.json - PSR-7 Version:
php vendor/pivotphp/core/scripts/switch-psr7-version.php --check - Error Messages: Full stack traces
- Configuration: Server settings, environment variables
- Test Case: Minimal code to reproduce the issue
# Check PSR-7 version
php vendor/pivotphp/core/scripts/switch-psr7-version.php --check
# Validate composer dependencies
composer validate
# Show installed packages
composer show
# Check for security vulnerabilities
composer audit
# Run ReactPHP server with debugging
APP_DEBUG=true php server.phpThis troubleshooting guide covers the most common issues encountered during ReactPHP integration with PivotPHP Core. Most problems stem from PSR-7 version conflicts, misunderstanding PivotPHP's immutable design, or incorrect usage of the Express.js pattern.