Thank you for your interest in contributing to the Axiom Library! We welcome contributions from the community.
Before creating bug reports, please check existing issues as you might find that you don't need to create one. When you create a bug report, please include as many details as possible:
- Use a clear and descriptive title
- Describe the exact steps to reproduce the problem
- Provide specific examples to demonstrate the steps
- Describe the behavior you observed and what you expected
- Include PHP version and environment details
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include:
- Use a clear and descriptive title
- Provide a detailed description of the suggested enhancement
- Explain why this enhancement would be useful
- List any similar features in other libraries
- Fill in the required template
- Follow the PHP coding style (PER/PSR-12)
- Include tests for new functionality
- Ensure all tests pass
- Update documentation as needed
- Write clear, descriptive commit messages
-
Fork and clone the repository
git clone https://github.com/your-username/axiom.git cd axiom -
Install dependencies
composer install
-
Run tests to ensure everything works
composer test
If you don't have PHP 8.4 installed locally, you can use Docker:
docker compose build
docker compose run --rm php composer install
docker compose run --rm php composer testWe use Laravel Pint for code formatting:
vendor/bin/pintThis will automatically fix code style issues according to the PER (PHP Evolving Recommendations) preset.
The project requires 100% code coverage for all new code. We use three types of testing:
-
Unit Tests (PHPUnit)
composer test:unit
- All new code must have corresponding tests
- Tests must achieve 100% line coverage
- Use PHPUnit 12+ attributes (
#[Test],#[CoversClass])
-
Static Analysis (PHPStan)
composer test:types
- Analysis level: max (strictest)
- All code must pass without errors
-
Mutation Testing (Infection)
composer test:infection
- Required Mutation Score Indicator (MSI): 100%
- Ensures test quality and effectiveness
Run all three with zend.assertions=1 (PHP's development default; check with
php -i | grep zend.assertions). assert() is how this codebase pins
invariants a type cannot state, and zend.assertions=-1 — the production
default — compiles every one of them away: the invariants go unchecked, their
lines leave the coverage report, and their mutants become unkillable. It is a
system setting, so it comes from the ini file or php -d, never from
phpunit.xml or ini_set().
composer testThis runs all three test suites in sequence.
- Minimum PHP version: 8.4
- Use modern PHP features (readonly properties, enums, etc.)
- Follow strict typing (
declare(strict_types=1))
-
Functional Programming
- Use Result and Option monads for error handling
- Avoid exceptions for control flow. Compilation has exactly two sanctioned
internal exceptions —
CompilationAborted(this source refuses, and says why) andCompilationAbsorbed(a child of this source already failed, so this one gives up without refusing). A source compiler sits at the top of a deep call tree it does not control, so an outcome decided several helpers down has to unwind to the walk; threading it back by hand through every compiler in between, host compilers included, would make one forgotten thread a certified type over an unchecked subtree. Both are caught inTypeInference::compile()and never escape it, andExpression::compile()returns aResult. Do not add a third. - Prefer immutability
-
Type Safety
- All methods must have type declarations
- Use PHPStan level max compliance
- Return explicit Result/Option types
-
Design Patterns
- Strategy Pattern for resolvers
- Chain of Responsibility for delegating resolvers
- Factory Pattern for type creation
- One class per file
- Follow PSR-4 autoloading
- Keep classes focused and single-purpose
- Write self-documenting code
- Update README.md if adding new features
- Include PHPDoc blocks for complex methods
- Add code examples for new functionality
- Keep documentation clear and concise
<?php
namespace Superscript\Axiom\Tests;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(YourClass::class)]
final class YourClassTest extends TestCase
{
#[Test]
public function it_does_something(): void
{
// Arrange
$instance = new YourClass();
// Act
$result = $instance->doSomething();
// Assert
self::assertTrue($result->isOk());
}
}- Unit tests mirror source structure
- Integration tests go in
tests/KitchenSink/ - Use
#[CoversClass]for unit tests - Use
#[CoversNothing]for integration tests
- Use present tense ("Add feature" not "Added feature")
- Use imperative mood ("Move cursor to..." not "Moves cursor to...")
- Limit first line to 72 characters
- Reference issues and pull requests when relevant
Examples:
Add support for custom operators in expressions
Fix null handling in StringType coercion
Update documentation for SymbolRegistry namespaces
-
Create a feature branch
git checkout -b feature/your-feature-name
-
Make your changes
- Write code
- Add tests
- Update documentation
-
Ensure quality
vendor/bin/pint # Format code composer test:types # Check static analysis composer test:unit # Run unit tests composer test:infection # Check mutation testing
-
Commit and push
git add . git commit -m "Your descriptive message" git push origin feature/your-feature-name
-
Create Pull Request
- Fill out the PR template completely
- Link related issues
- Await code review
-
Address feedback
- Make requested changes
- Push updates to the same branch
- Respond to review comments
- PHP Fig - PSR Standards
- PHPStan Documentation
- Infection Mutation Testing
- php-standard-library/php-standard-library - Our standard library
Feel free to open an issue for:
- Questions about contributing
- Clarifications on architecture
- Help with development setup
Thank you for contributing! 🎉