- Created by Ines-Paul Baumann, last modified by Leon Kiz on Nov 13, 2024
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 11 Next »
Every developer should follow the PHP Standards Recommendations
1.1 Variables:
- Use camelCase for variable names.
- Variable names should be descriptive and concise.
- Avoid abbreviations unless widely recognized.
$userName = 'John'; $totalAmount = 100;
1.2 Functions and Methods:
- Use camelCase for function and method names.
- Function and method names should clearly express their purpose.
- Methods should start with a verb when possible.
function calculateTotalAmount($prices) { // Logic here }
1.3 Classes:
- Use PascalCase for class names.
- Class names should be nouns and describe the entity or the functionality.
class UserController { // Class definition }
1.4 Constants:
- Use UPPER_CASE with underscores (screaming snake case) for constants.
- Constants should be descriptive and represent fixed values.
define('MAX_RETRY_ATTEMPTS', 5); const DEFAULT_TIMEOUT = 30;
1.5 Interfaces:
- Use PascalCase with the suffix Interface.
- Interfaces should describe behavior and represent contracts.
interface CacheInterface { public function get(string $key): int; public function set(string $key, string $value); }
1.6 Abstract classes
- Use PascalCase with the prefix Abstract.
abstract class AbstractJob { abstract public function getName(): string; // Other logic here }
1.7 Namespaces:
- Use PascalCase for namespaces.
- Namespace names should map to the directory structure.
namespace App\Controllers;
2.1 Dependency Injection (DI):
- All class dependencies must be explicit and provided through Dependency Injection.
- Avoid creating dependencies within the class or using global state (e.g.,
global
variables orsingleton
patterns). - Use constructor injection or method injection for better testability and maintainability.
class OrderProcessor { private PaymentGatewayInterface $paymentGateway; public function __construct(PaymentGatewayInterface $paymentGateway) { $this->paymentGateway = $paymentGateway; } }
2.2 New Code Placement:
- All new code must be placed in the
/src
folder. - Code must adhere to PSR standards, including:
- Proper use of namespaces to reflect the directory structure.
- Avoidance of PHP 4-style class names (no underscores for class name simulation).
2.3 SOLID Principles:
- All new code must follow the SOLID principles:
- Single Responsibility Principle: A class should have one and only one reason to change.
- Open/Closed Principle: Code should be open for extension but closed for modification.
- Liskov Substitution Principle: Subtypes must be substitutable for their base types.
- Interface Segregation Principle: Classes should not be forced to implement unnecessary interfaces.
- Dependency Inversion Principle: Depend on abstractions, not on concrete implementations.
2.4 Unit Tests:
- All new classes must have corresponding unit tests.
- Tests should cover the main functionality and edge cases.
- Follow the AAA structure for tests (Arrange, Act, Assert).
use PHPUnit\Framework\TestCase; class PaymentServiceTest extends TestCase { public function testProcessPayment() { // Arrange $mockGateway = $this->createMock(PaymentGatewayInterface::class); $service = new PaymentService($mockGateway); // Act $result = $service->processPayment(100); // Assert $this->assertTrue($result); } }
2.5 Encapsulation Over Inheritance:
- Prefer encapsulation over extending classes.
- Use inheritance only when a clear “is-a” relationship exists.
Problem with Inheritance:
Using inheritance might seem natural when trying to extend functionality, but it can lead to tightly coupled designs and rigid hierarchies.
class FileStorage { public function save(string $fileName, string $data): void { // Save the file to disk echo "Saving to disk: $fileName\n"; } } class LoggingFileStorage extends FileStorage { public function save(string $fileName, string $data): void { // Log the action before saving echo "Logging: Saving $fileName\n"; parent::save($fileName, $data); } }
Issues:
- The
LoggingFileStorage
class is tightly coupled to theFileStorage
implementation. - If the base class changes,
LoggingFileStorage
might break or require updates. - Adding more variations (e.g., caching, encrypting) will create a complex and rigid inheritance hierarchy.
Better with Encapsulation:
Encapsulation allows us to compose functionality dynamically and avoid the pitfalls of deep inheritance.
interface StorageInterface { public function save(string $fileName, string $data): void; } class FileStorage implements StorageInterface { public function save(string $fileName, string $data): void { // Save the file to disk echo "Saving to disk: $fileName\n"; } } class LoggingStorage implements StorageInterface { public function __construct(private readonly StorageInterface $storage) { $this->storage = $storage; } public function save(string $fileName, string $data): void { // Log the action before saving echo "Logging: Saving $fileName\n"; $this->storage->save($fileName, $data); } } class CachingStorage implements StorageInterface { public function __construct(private readonly StorageInterface $storage) { $this->storage = $storage; } public function save(string $fileName, string $data): void { // Save to cache before saving to storage echo "Caching: $fileName\n"; $this->storage->save($fileName, $data); } }
Advantages of Encapsulation Over Inheritance:
- Flexibility: Functionality can be composed dynamically without changing the underlying classes.
- Reusability: Decorators can be reused across different storage implementations (e.g., database, cloud storage).
- Scalability: Adding new behavior (e.g., encryption) doesn’t require modifying existing classes or creating a rigid hierarchy.
This approach adheres to the Open/Closed Principle (code is open for extension but closed for modification) and results in more maintainable, testable, and decoupled code.
2.6 Interface Usage:
- Always program to interfaces, not implementations.
- Use interfaces to define contracts and ensure flexibility for future changes.
2.7 Small, Focused Methods:
- Keep methods small and focused on a single task.
- If a method grows beyond 15–20 lines, consider refactoring into smaller, reusable methods.
2.8 Avoid Hardcoding:
- Avoid hardcoding values or logic directly into your classes.
- Use configuration files, environment variables, or constants for flexibility.
class RetryHandler { private const MAX_RETRY_COUNT = 5; public function handle() { for ($i = 0; $i < self::MAX_RETRY_COUNT; $i++) { // Retry logic } } }
2.9 Readable Code:
- Prioritize readability over clever or overly complex code.
- Use meaningful variable and method names, and write inline comments for non-obvious logic. Important here is not to write comments that describe what the code is doing because it is obvious from the code itself, but to write comments describing the logic behind, WHY it is doing this.
2.10 Avoid Side Effects:
- Functions and methods should avoid unexpected side effects.
- For example, avoid modifying global state or altering input parameters.
- Functions that are designed to do some checks should not mutate or create something and vice versa
Every developer should follow the following principles:
- YAGNI: You ain’t gonna need it (wiki)
- KISS: Keep It Simple, Stupid (wiki)
- DRY: Don’t Repeat Yourself (wiki)
- No labels