This guide helps you set up a development environment for contributing to ApiKit Bundle.
git clone https://github.com/bulatronic/api-kit.git
cd api-kit
composer install
The project uses FrankenPHP for development:
docker compose up -d
docker compose exec frankenphp bash
# Inside the container
composer test
# Or outside
docker compose exec frankenphp composer test
# PHPStan level 8
composer phpstan
# Check code style
composer cs-check
# Fix code style automatically
composer cs-fix
# Run everything at once
composer test && composer phpstan && composer cs-check
api-kit/
├── bin/ # Executable scripts
├── config/ # Bundle configuration
│ ├── packages/ # Package configs
│ ├── routes/ # Route definitions
│ └── services.yaml # Service definitions
├── docs/ # Developer documentation
├── public/ # Public web root
├── src/ # Source code
│ ├── Controller/ # Traits for controllers
│ ├── DependencyInjection/ # Configuration
│ ├── EventListener/ # Event listeners
│ ├── Response/ # Response factory
│ ├── Validator/ # Custom validators
│ └── ApiKitBundle.php
├── tests/ # Test suite
│ ├── Unit/ # Unit tests
│ └── Functional/ # Functional tests
├── .editorconfig # Editor configuration
├── .gitignore # Git ignore rules
├── .php-cs-fixer.dist.php # PHP CS Fixer config
├── composer.json # Composer dependencies
├── Dockerfile # Docker image
├── docker-compose.yml # Docker compose config
├── phpstan.dist.neon # PHPStan configuration
└── phpunit.dist.xml # PHPUnit configuration
// ✅ Always declare strict types
declare(strict_types=1);
// ✅ Use full type hints
public function method(string $param): ?int
// ✅ Use readonly for immutable objects
final readonly class MyClass
// ❌ Avoid mixed types
public function method(mixed $param)
/**
* Brief description.
*
* Extended description if needed.
*
* [@param](https://github.com/param) string $param Parameter description
* [@return](https://github.com/return) JsonResponse Response description
*/
public function method(string $param): JsonResponse
Follow PSR-12 with these additions:
// ✅ Use final classes by default
final class MyClass
// ✅ Use readonly where possible
final readonly class ImmutableClass
// ✅ Use named arguments for clarity
$this->factory->success(
data: $data,
statusCode: 200,
meta: ['timestamp' => time()],
);
// ✅ Use match over switch
$result = match ($type) {
'json' => $this->json(),
'xml' => $this->xml(),
default => throw new \InvalidArgumentException(),
};
<?php
declare(strict_types=1);
namespace ApiKit\Tests\Unit\Response;
use PHPUnit\Framework\TestCase;
final class ResponseFactoryTest extends TestCase
{
public function testSuccessCreatesSuccessResponse(): void
{
// Arrange
$factory = new ResponseFactory();
// Act
$response = $factory->success(['key' => 'value']);
// Assert
$this->assertSame(200, $response->getStatusCode());
// More assertions...
}
}
// ✅ Descriptive test names
public function testSuccessResponseIncludesTimestampWhenConfigured(): void
// ❌ Vague test names
public function testSuccess(): void
feature/short-descriptionfix/short-descriptiondocs/short-descriptionFollow conventional commits:
feat: add EntityExists validator
fix: correct timestamp format in responses
docs: update ARCHITECTURE.md
test: add tests for ResponseFactory
refactor: simplify exception handling
Design First
Implement
Quality Checks
composer test
composer phpstan
composer cs-fix
Documentation
Reproduce
Fix
Verify
# Set breakpoint in code
xdebug_break();
# Or use your IDE's debugging tools
# View container logs
docker compose logs -f frankenphp
# View Symfony logs
tail -f var/log/dev.log
# Use Blackfire (if configured)
blackfire run php bin/console
# Or Symfony profiler in browser
http://localhost:8000/_profiler
Create .env.local for local development:
APP_ENV=dev
APP_DEBUG=true
# Clear cache
php bin/console cache:clear
# Reinstall dependencies
rm -rf vendor
composer install
# Rebuild containers
docker compose down
docker compose build --no-cache
docker compose up -d
# Generate baseline (use sparingly)
composer phpstan -- --generate-baseline
# Clear result cache
rm -rf var/cache/phpstan
# Use Apache Bench
ab -n 1000 -c 10 http://localhost:8000/api/test
# Or hey
hey -n 1000 -c 10 http://localhost:8000/api/test
// Add at start of request
$start = memory_get_usage();
// Add at end of request
$end = memory_get_usage();
echo "Memory used: " . ($end - $start) . " bytes\n";
v1.0.0git push origin v1.0.0How can I help you explore Laravel packages today?