This guide helps you set up a development environment for contributing to ApiKit Bundle.
- Docker and Docker Compose
- PHP 8.2+ (for local development without Docker)
- Composer
- Git
git clone https://github.com/bulatronic/api-kit.git
cd api-kitcomposer installThe project uses FrankenPHP for development:
docker compose up -ddocker 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-checkapi-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
- Minimum: PHP 8.2
- Use modern PHP features:
- Readonly classes and properties
- Property hooks (asymmetric visibility)
- Named arguments
- Match expressions
- Attributes
// ✅ 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)- Classes: PascalCase
- Methods: camelCase
- Properties: camelCase
- Constants: UPPER_SNAKE_CASE
- Attributes: PascalCase
/**
* Brief description.
*
* Extended description if needed.
*
* @param string $param Parameter description
* @return JsonResponse Response description
*/
public function method(string $param): JsonResponseFollow 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...
}
}- Aim for 80%+ code coverage
- Test all public methods
- Test edge cases and error conditions
- Test configuration options
// ✅ Descriptive test names
public function testSuccessResponseIncludesTimestampWhenConfigured(): void
// ❌ Vague test names
public function testSuccess(): void- Features:
feature/short-description - Bugfixes:
fix/short-description - Documentation:
docs/short-description
Follow 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
- Create a feature branch
- Make your changes
- Add/update tests
- Run quality checks
- Update documentation if needed
- Create PR with clear description
-
Design First
- Does it fit the minimalist philosophy?
- Is it a core feature or an extension?
- Check existing issues/discussions
-
Implement
- Write tests first (TDD)
- Implement feature
- Update documentation
-
Quality Checks
composer test composer phpstan composer cs-fix -
Documentation
- Update README.md if needed
- Add examples to EXAMPLES.md
- Update CHANGELOG.md
-
Reproduce
- Write a failing test
- Verify the bug exists
-
Fix
- Implement the fix
- Ensure test passes
-
Verify
- Run full test suite
- Check related functionality
- Keep examples up-to-date
- Use real, working code
- Test all examples
- Check spelling and grammar
# 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/_profilerCreate .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";- Update CHANGELOG.md
- Update version in composer.json (if needed)
- Run all quality checks
- Create git tag:
v1.0.0 - Push tag:
git push origin v1.0.0 - Create GitHub release
- Read existing documentation
- Check open/closed issues
- Ask in discussions
- Contact maintainer: bulat.coder@gmail.com
- Be respectful and constructive
- Focus on the code, not the person
- Help others learn and grow
- Follow the project's goals and philosophy