Advanced PHP & Best Practices
Composer & PSR Standards
Composer is the standard package manager for PHP, which allows installing and updating external libraries, as well as autoloading classes according to PSR standards. PSRs (PHP Standards Recommendations) define best practices and conventions for PHP code, such as PSR-1 and PSR-12 for coding standards or PSR-4 for autoloading.
Why use Composer and PSR?
- Automated dependency management
- Standardized autoloading for classes
- Clean, organized, and maintainable code
- Compatibility with external libraries and a large community
Practical Composer Example
1. Initialize the project with Composer:
composer init
2. Install a library, for example Monolog:
composer require monolog/monolog
3. Use PSR-4 autoloading in the project:
<?php
require 'vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// Create a logger
$log = new Logger('application');
$log->pushHandler(new StreamHandler('app.log', Logger::WARNING));
// Log a message
$log->warning('This is a warning!');
?>
MVC Pattern Explained
MVC (Model-View-Controller) is an architectural pattern that separates responsibilities in an application:
- Model - manages application data and logic (e.g., database interactions)
- View - handles data presentation (e.g., HTML, templates)
- Controller - receives requests, interacts with the model, and decides which view to display
Typical structure of an MVC project
project/
├── index.php # Front controller
├── app/
│ ├── Controllers/
│ │ └── UserController.php
│ ├── Models/
│ │ └── User.php
│ └── Views/
│ └── user_list.php
└── vendor/ # Composer autoload
Simple Practical Example
Model - User.php
<?php
class User {
private $users = [
['id' => 1, 'name' => 'John Smith'],
['id' => 2, 'name' => 'Mary Ionella']
];
public function getAll() {
return $this->users;
}
}
?>
Controller - UserController.php
<?php
require_once 'Models/User.php';
class UserController {
public function index() {
$userModel = new User();
$users = $userModel->getAll();
include 'Views/user_list.php';
}
}
?>
View - user_list.php
<?php foreach($users as $user): ?>
<p>ID: <?php echo $user['id']; ?>, name: <?php echo $user['name']; ?></p>
<?php endforeach; ?>
Front Controller - index.php
<?php
require_once 'app/Controllers/UserController.php';
$controller = new UserController();
$controller->index();
?>
Thus, any request to index.php is directed to the controller, which retrieves data from the model
and passes it to the view for display. This is a simplified example of MVC in PHP.
Unit Testing with PHPUnit
Unit testing represents testing individual functions or classes in a project to ensure they behave correctly. PHPUnit is the standard framework for unit testing in PHP.
Installing PHPUnit
It is recommended to install via Composer:
composer require --dev phpunit/phpunit ^10
Structure of a Simple Test
project/
├── src/
│ └── Calculator.php
└── tests/
└── CalculatorTest.php
Practical Example - Calculator.php
<?php
class Calculator {
public function add($a, $b) {
return $a + $b;
}
public function subtract($a, $b) {
return $a - $b;
}
}
?>
Practical Example - CalculatorTest.php
<?php
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../src/Calculator.php';
class CalculatorTest extends TestCase {
public function testAdd() {
$calc = new Calculator();
$this->assertEquals(5, $calc->add(2,3));
}
public function testSubtract() {
$calc = new Calculator();
$this->assertEquals(1, $calc->subtract(3,2));
}
}
?>
Running Tests
After installing PHPUnit, run the tests from the terminal like this:
./vendor/bin/phpunit tests/CalculatorTest.php The result will show whether the tests passed or
failed.
This mechanism allows us to verify individual functionalities of the code and quickly detect errors, which is essential for large or collaborative projects.
PHP 8 Features: Match, Nullsafe Operator & Attributes
1. Match Expression
Match is a safer and more concise replacement for switch. It can return values and does not require breaks.
<?php
$day = 'Tuesday';
$message = match($day) {
'Monday' => 'Start of the week!',
'Tuesday' => 'Work day',
'Wednesday' => 'Midweek',
'Thursday', 'Friday' => 'Almost weekend',
default => 'Weekend',
};
echo $message; // Output: Work day
?>
2. Nullsafe Operator (??)
The nullsafe operator allows safely accessing properties or methods of an object that may be null, without causing an error.
<?php
class User {
public ?Profile $profile = null;
}
class Profile {
public string $bio = 'Hello!';
}
$user = new User();
// Safe access without error if $profile is null
$bio = $user->profile?>bio;
echo $bio; // Output: null
?>
3. Attributes
Attributes allow adding metadata to classes, methods, or properties without using docblocks.
<?php
#[Attribute]
class ExampleAttribute {
public function __construct(public string $info) {}
}
#[ExampleAttribute('This is an example')]
class MyClass {}
$reflection = new ReflectionClass(MyClass::class);
$attrs = $reflection->getAttributes();
foreach($attrs as $attr) {
$instance = $attr->newInstance();
echo $instance->info; // Output: This is an example
}
?>
These PHP 8 features make code cleaner, safer, and more expressive.
Example: Organizing the Project with PSR-4
PSR-4 is the standard for class autoloading in PHP. It allows automatic loading of classes from namespaces corresponding to directory structures.
Project Structure
my_project/
├─ composer.json
├─ src/
│ ├─ Controllers/
│ │ └─ UserController.php
│ ├─ Models/
│ │ └─ User.php
│ └─ Services/
│ └─ AuthService.php
├─ public/
│ └─ index.php
Example of a Class with Namespace
<?php
namespace App\Models;
class User {
public string $name;
public string $email;
public function __construct(string $name, string $email) {
$this->name = $name;
$this->email = $email;
}
}
?>
Composer & PSR-4 Autoloading
In the composer.json file, we define PSR-4 autoloading:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
Then we run in the terminal: composer dump-autoload. In public/index.php, we can use
the classes directly:
<?php
require __DIR__ . '/../vendor/autoload.php';
use App\Models\User;
$user = new User('John Smith', 'ion.popescu@example.com');
echo $user->name; // Output: John Smith
?>
This way, the code is clearly organized by namespaces, classes are loaded automatically, and the project follows PSR-4 standards.
