<?php
// Trait definition
trait Timestampable {
private ?string $createdAt = null;
private ?string $updatedAt = null;
public function setCreatedAt(): void {
$this->createdAt = date('Y-m-d H:i:s');
}
public function setUpdatedAt(): void {
$this->updatedAt = date('Y-m-d H:i:s');
}
public function getCreatedAt(): ?string { return $this->createdAt; }
public function getUpdatedAt(): ?string { return $this->updatedAt; }
}
trait SoftDeletable {
private ?string $deletedAt = null;
public function softDelete(): void {
$this->deletedAt = date('Y-m-d H:i:s');
}
public function isDeleted(): bool { return $this->deletedAt !== null; }
public function restore(): void { $this->deletedAt = null; }
}
trait Serializable {
public function toArray(): array {
return get_object_vars($this);
}
public function toJson(): string {
return json_encode($this->toArray());
}
}
// Use multiple traits
class Post {
use Timestampable, SoftDeletable, Serializable;
public function __construct(
private string $title,
private string $content
) {
$this->setCreatedAt();
}
}
$post = new Post("Hello World", "My first post");
echo $post->getCreatedAt(); // "2024-01-15 10:30:00"
$post->softDelete();
echo $post->isDeleted(); // true
$post->restore();
echo $post->isDeleted(); // false
echo $post->toJson(); // JSON of all properties
<?php
trait Validatable {
// Abstract method in trait — using class MUST implement
abstract protected function rules(): array;
public function validate(array $data): array {
$errors = [];
foreach ($this->rules() as $field => $rule) {
if ($rule === 'required' && empty($data[$field])) {
$errors[$field] = "$field is required";
}
if (str_starts_with($rule, 'min:')) {
$min = (int) substr($rule, 4);
if (strlen($data[$field] ?? '') < $min) {
$errors[$field] = "$field must be at least $min characters";
}
}
if ($rule === 'email' && !filter_var($data[$field] ?? '', FILTER_VALIDATE_EMAIL)) {
$errors[$field] = "$field must be a valid email";
}
}
return $errors;
}
public function isValid(array $data): bool {
return empty($this->validate($data));
}
}
class UserForm {
use Validatable;
protected function rules(): array {
return [
'name' => 'required',
'email' => 'email',
'password' => 'min:8',
];
}
}
$form = new UserForm();
$errors = $form->validate([
'name' => 'Ko Ko',
'email' => 'invalid-email',
'password' => '123',
]);
print_r($errors);
// ['email' => '...must be a valid email', 'password' => '...at least 8 characters']
<?php
trait A {
public function hello(): string { return "Hello from A"; }
public function hi(): string { return "Hi from A"; }
}
trait B {
public function hello(): string { return "Hello from B"; }
public function hi(): string { return "Hi from B"; }
}
class MyClass {
use A, B {
// Conflict resolution — specify which to use
A::hello insteadof B; // use A's hello(), ignore B's
B::hi insteadof A; // use B's hi(), ignore A's
// Alias — keep both with different names
B::hello as helloFromB;
A::hi as hiFromA;
}
}
$obj = new MyClass();
echo $obj->hello(); // "Hello from A" (A wins)
echo $obj->hi(); // "Hi from B" (B wins)
echo $obj->helloFromB(); // "Hello from B" (alias)
echo $obj->hiFromA(); // "Hi from A" (alias)
<?php
// Logger trait
trait Loggable {
private array $logs = [];
public function log(string $level, string $message): void {
$this->logs[] = [
'level' => strtoupper($level),
'message' => $message,
'time' => date('H:i:s'),
];
}
public function getLogs(): array { return $this->logs; }
public function info(string $msg): void { $this->log('info', $msg); }
public function error(string $msg): void { $this->log('error', $msg); }
}
// Pagination trait
trait Paginatable {
protected int $perPage = 10;
public function paginate(array $items, int $page = 1): array {
$total = count($items);
$offset = ($page - 1) * $this->perPage;
$data = array_slice($items, $offset, $this->perPage);
return [
'data' => $data,
'total' => $total,
'per_page' => $this->perPage,
'current_page' => $page,
'last_page' => (int) ceil($total / $this->perPage),
];
}
}
// Singleton trait
trait SingletonTrait {
private static ?self $instance = null;
private function __construct() {}
public static function getInstance(): static {
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
}
// Usage
class OrderService {
use Loggable, Paginatable, SingletonTrait;
public function processOrder(int $orderId): void {
$this->info("Processing order #$orderId");
// ...
$this->info("Order #$orderId processed successfully");
}
}
$service = OrderService::getInstance();
$service->processOrder(42);
print_r($service->getLogs());
← PHP 10 | Next: PHP 12 → Namespaces & Composer →