Sau khi nắm vững Traits, bước tiếp theo là học Static Methods và Properties – cơ chế truy cập properties/methods mà không cần tạo object. Static members thuộc về class chứ không thuộc về object cụ thể nào. Đây là kiến thức quan trọng để tạo utility classes, singletons và các design patterns khác.
1. Static là gì?
Static là từ khóa cho phép truy cập properties/methods trực tiếp qua class name mà không cần tạo object. Static members được chia sẻ chung cho tất cả instances của class.
1.1. Vấn đề khi không có Static
<?php
// ❌ Phải tạo object mỗi lần dùng
class Calculator {
public function add($a, $b) {
return $a + $b;
}
public function multiply($a, $b) {
return $a * $b;
}
}
// Phải tạo object để dùng
$calc = new Calculator();
echo $calc->add(5, 3); // 8
echo $calc->multiply(4, 7); // 28
// Lặp lại tạo object nhiều lần
$calc2 = new Calculator();
echo $calc2->add(10, 20);
?>1.2. Giải pháp: Dùng Static
<?php
// ✅ Dùng Static - không cần tạo object
class Calculator {
public static function add($a, $b) {
return $a + $b;
}
public static function multiply($a, $b) {
return $a * $b;
}
}
// Gọi trực tiếp qua class name
echo Calculator::add(5, 3); // 8
echo Calculator::multiply(4, 7); // 28
echo Calculator::add(10, 20); // 30
// Không cần tạo object, gọn gàng hơn
?>2. Static Properties
Static properties được chia sẻ chung cho tất cả instances. Thay đổi ở 1 nơi ảnh hưởng mọi nơi.
2.1. Khai báo và truy cập Static Property
<?php
class Counter {
public static $count = 0; // Static property
public static function increment() {
self::$count++; // Truy cập static property bằng self::
}
public static function getCount() {
return self::$count;
}
}
// Truy cập qua class name
echo Counter::$count; // 0
// Tăng counter
Counter::increment();
Counter::increment();
Counter::increment();
echo Counter::getCount(); // 3
// Thay đổi trực tiếp
Counter::$count = 10;
echo Counter::$count; // 10
?>2.2. Static Property được chia sẻ
<?php
class User {
public static $totalUsers = 0;
public $name;
public function __construct($name) {
$this->name = $name;
self::$totalUsers++; // Tăng counter khi tạo user
}
public static function getTotalUsers() {
return self::$totalUsers;
}
}
$user1 = new User("An");
echo User::getTotalUsers(); // 1
$user2 = new User("Bình");
echo User::getTotalUsers(); // 2
$user3 = new User("Chi");
echo User::getTotalUsers(); // 3
// Static property được chia sẻ giữa tất cả objects
?>3. Static Methods
Static methods không thể truy cập $this vì không có object cụ thể.
3.1. Khai báo Static Method
<?php
class Math {
public static function square($n) {
return $n * $n;
}
public static function cube($n) {
return $n * $n * $n;
}
public static function power($base, $exp) {
return pow($base, $exp);
}
}
// Gọi static methods
echo Math::square(5); // 25
echo Math::cube(3); // 27
echo Math::power(2, 10); // 1024
?>3.2. Static method không dùng được $this
<?php
class Test {
public $name = "Instance";
public static $staticName = "Static";
public function instanceMethod() {
echo $this->name; // ✅ OK - có $this
echo self::$staticName; // ✅ OK - truy cập static
}
public static function staticMethod() {
// echo $this->name; // ❌ Lỗi - không có $this
echo self::$staticName; // ✅ OK - truy cập static
}
}
Test::staticMethod(); // Static
$test = new Test();
$test->instanceMethod(); // Instance Static
?>4. Từ khóa self, static, parent
4.1. self:: – Truy cập static của class hiện tại
<?php
class Config {
private static $settings = [];
public static function set($key, $value) {
self::$settings[$key] = $value; // self:: trỏ đến Config
}
public static function get($key) {
return self::$settings[$key] ?? null;
}
public static function all() {
return self::$settings;
}
}
Config::set('app_name', 'My App');
Config::set('debug', true);
echo Config::get('app_name'); // My App
print_r(Config::all());
?>4.2. static:: – Late Static Binding
<?php
class ParentClass {
public static $name = "Parent";
public static function getName() {
return self::$name; // self:: luôn trỏ đến ParentClass
}
public static function getNameLate() {
return static::$name; // static:: trỏ đến class được gọi
}
}
class ChildClass extends ParentClass {
public static $name = "Child";
}
// Với self::
echo ParentClass::getName(); // Parent
echo ChildClass::getName(); // Parent (self:: luôn là ParentClass)
// Với static::
echo ParentClass::getNameLate(); // Parent
echo ChildClass::getNameLate(); // Child (static:: là ChildClass)
?>4.3. parent:: – Truy cập static của class cha
<?php
class Animal {
public static $type = "Animal";
public static function getType() {
return self::$type;
}
}
class Dog extends Animal {
public static $type = "Dog";
public static function getType() {
return self::$type;
}
public static function getParentType() {
return parent::getType(); // Gọi method của class cha
}
}
echo Dog::getType(); // Dog
echo Dog::getParentType(); // Animal (từ parent)
?>5. Late Static Binding
Late Static Binding cho phép static:: trỏ đến class được gọi thay vì class được định nghĩa (như self::).
<?php
class Model {
protected static $table;
public static function getTable() {
return static::$table; // static:: - Late Static Binding
}
public static function find($id) {
echo "SELECT * FROM " . static::getTable() . " WHERE id = $id";
}
}
class User extends Model {
protected static $table = "users";
}
class Product extends Model {
protected static $table = "products";
}
User::find(1); // SELECT * FROM users WHERE id = 1
Product::find(5); // SELECT * FROM products WHERE id = 5
// static::getTable() trỏ đến User hoặc Product
// self::getTable() sẽ luôn trỏ đến Model
?>6. Khi nào dùng Static
6.1. ✅ NÊN dùng Static
Utility Classes:
<?php class StringHelper { public static function slugify($text) { return strtolower(preg_replace('/[^a-z0-9]+/', '-', $text)); } public static function truncate($text, $length) { return substr($text, 0, $length) . '...'; } } echo StringHelper::slugify("Hello World"); // hello-world ?>
Factory Methods:
<?php class User { private $name; private $email; private function __construct($name, $email) { $this->name = $name; $this->email = $email; } public static function create($name, $email) { return new self($name, $email); } public static function createFromArray($data) { return new self($data['name'], $data['email']); } } $user1 = User::create("An", "an@email.com"); $user2 = User::createFromArray(['name' => 'Bình', 'email' => 'binh@email.com']); ?>
Constants và Config:
<?php class App { public static $env = "production"; public static function isProduction() { return self::$env === "production"; } public static function isDevelopment() { return self::$env === "development"; } } if (App::isProduction()) { // Production logic } ?>
6.2. ❌ KHÔNG nên dùng Static
- Khi cần state riêng cho mỗi object
- Khi cần polymorphism (override methods)
- Khi cần dependency injection
- Khi cần testing với mocks
7. Ví dụ thực tế
7.1. Database Connection (Singleton Pattern)
<?php
class Database {
private static $instance = null;
private $conn;
private function __construct() {
$this->conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function query($sql) {
return $this->conn->query($sql);
}
// Chặn clone và unserialize
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
}
// Sử dụng
$db = Database::getInstance();
$result = $db->query("SELECT * FROM users");
$db2 = Database::getInstance();
var_dump($db === $db2); // true - cùng 1 instance
?>7.2. Validator Class
<?php
class Validator {
public static function email($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
public static function required($value) {
return !empty(trim($value));
}
public static function minLength($value, $min) {
return strlen($value) >= $min;
}
public static function maxLength($value, $max) {
return strlen($value) <= $max;
}
public static function numeric($value) {
return is_numeric($value);
}
public static function between($value, $min, $max) {
return $value >= $min && $value <= $max;
}
public static function url($url) {
return filter_var($url, FILTER_VALIDATE_URL) !== false;
}
public static function date($date, $format = 'Y-m-d') {
$d = DateTime::createFromFormat($format, $date);
return $d && $d->format($format) === $date;
}
}
// Sử dụng
if (Validator::email("user@email.com")) {
echo "Email hợp lệ
";
}
if (Validator::minLength("password", 6)) {
echo "Password đủ dài
";
}
if (Validator::between(25, 18, 65)) {
echo "Tuổi hợp lệ
";
}
?>7.3. Session Manager
<?php
class Session {
private static $started = false;
public static function start() {
if (!self::$started) {
session_start();
self::$started = true;
}
}
public static function set($key, $value) {
self::start();
$_SESSION[$key] = $value;
}
public static function get($key, $default = null) {
self::start();
return $_SESSION[$key] ?? $default;
}
public static function has($key) {
self::start();
return isset($_SESSION[$key]);
}
public static function remove($key) {
self::start();
if (isset($_SESSION[$key])) {
unset($_SESSION[$key]);
}
}
public static function destroy() {
self::start();
session_destroy();
self::$started = false;
}
public static function flash($key, $value) {
self::set($key, $value);
self::set('_flash_' . $key, true);
}
public static function getFlash($key) {
$value = self::get($key);
if (self::get('_flash_' . $key)) {
self::remove($key);
self::remove('_flash_' . $key);
}
return $value;
}
}
// Sử dụng
Session::set('user_id', 123);
Session::set('username', 'john_doe');
echo Session::get('user_id'); // 123
if (Session::has('username')) {
echo "User đã đăng nhập";
}
// Flash message (hiển thị 1 lần)
Session::flash('success', 'Đăng nhập thành công');
echo Session::getFlash('success'); // Hiển thị
echo Session::getFlash('success'); // null (đã xóa)
?>7.4. Cache Class
<?php
class Cache {
private static $cache = [];
private static $cacheDir = __DIR__ . '/cache/';
public static function set($key, $value, $ttl = 3600) {
$data = [
'value' => $value,
'expires' => time() + $ttl
];
// Memory cache
self::$cache[$key] = $data;
// File cache
if (!is_dir(self::$cacheDir)) {
mkdir(self::$cacheDir, 0755, true);
}
file_put_contents(
self::$cacheDir . md5($key) . '.cache',
serialize($data)
);
}
public static function get($key) {
// Check memory cache
if (isset(self::$cache[$key])) {
if (self::$cache[$key]['expires'] > time()) {
return self::$cache[$key]['value'];
}
unset(self::$cache[$key]);
}
// Check file cache
$file = self::$cacheDir . md5($key) . '.cache';
if (file_exists($file)) {
$data = unserialize(file_get_contents($file));
if ($data['expires'] > time()) {
self::$cache[$key] = $data;
return $data['value'];
}
unlink($file);
}
return null;
}
public static function has($key) {
return self::get($key) !== null;
}
public static function forget($key) {
unset(self::$cache[$key]);
$file = self::$cacheDir . md5($key) . '.cache';
if (file_exists($file)) {
unlink($file);
}
}
public static function flush() {
self::$cache = [];
$files = glob(self::$cacheDir . '*.cache');
foreach ($files as $file) {
unlink($file);
}
}
public static function remember($key, $ttl, $callback) {
$value = self::get($key);
if ($value !== null) {
return $value;
}
$value = $callback();
self::set($key, $value, $ttl);
return $value;
}
}
// Sử dụng
Cache::set('user_1', ['name' => 'An', 'email' => 'an@email.com'], 600);
$user = Cache::get('user_1');
print_r($user);
// Remember - get hoặc set nếu chưa có
$products = Cache::remember('products', 3600, function() {
// Query database
return [
['id' => 1, 'name' => 'Product A'],
['id' => 2, 'name' => 'Product B']
];
});
Cache::forget('user_1'); // Xóa 1 key
Cache::flush(); // Xóa tất cả
?>7.5. Query Builder
<?php
class QueryBuilder {
private static $conn;
private $table;
private $where = [];
private $orderBy = [];
private $limit;
public function __construct($table) {
$this->table = $table;
}
public static function setConnection($conn) {
self::$conn = $conn;
}
public static function table($table) {
return new self($table);
}
public function where($column, $operator, $value) {
$this->where[] = "$column $operator ?";
$this->bindings[] = $value;
return $this;
}
public function orderBy($column, $direction = 'ASC') {
$this->orderBy[] = "$column $direction";
return $this;
}
public function limit($limit) {
$this->limit = $limit;
return $this;
}
public function get() {
$sql = "SELECT * FROM {$this->table}";
if (!empty($this->where)) {
$sql .= " WHERE " . implode(' AND ', $this->where);
}
if (!empty($this->orderBy)) {
$sql .= " ORDER BY " . implode(', ', $this->orderBy);
}
if ($this->limit) {
$sql .= " LIMIT {$this->limit}";
}
$stmt = self::$conn->prepare($sql);
$stmt->execute($this->bindings ?? []);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
// Setup
$pdo = new PDO("mysql:host=localhost;dbname=test", "root", "");
QueryBuilder::setConnection($pdo);
// Sử dụng
$users = QueryBuilder::table('users')
->where('age', '>=', 18)
->where('status', '=', 'active')
->orderBy('created_at', 'DESC')
->limit(10)
->get();
print_r($users);
?>8. Best Practices
8.1. Quy tắc Static
✅ NÊN:
- Dùng cho utility methods (không cần state)
- Dùng cho factory methods
- Dùng cho singletons
- Dùng cho constants và config
- Đặt tên method/property rõ nghĩa
❌ KHÔNG NÊN:
- Lạm dụng static (khó test, khó maintain)
- Dùng static khi cần state riêng cho mỗi object
- Dùng static khi cần polymorphism
- Static methods gọi
$this
Tóm tắt
Qua bài này bạn đã nắm được:
- ✅ Static: Truy cập qua class name không cần object
- ✅ Static Property:
public static $var– chia sẻ chung - ✅ Static Method:
public static function()– không dùng $this - ✅ self:: Trỏ đến class được định nghĩa
- ✅ static:: Trỏ đến class được gọi (Late Static Binding)
- ✅ parent:: Trỏ đến class cha
- ✅ Truy cập:
ClassName::$property,ClassName::method() - ✅ Khi dùng: Utility, Factory, Singleton, Config
Bài tiếp theo, bạn sẽ học về Magic Methods – các methods đặc biệt của PHP.
🎯 Bài tập thực hành
- Bài 1: Tạo class
FileHelpervới static methods: getExtension($filename), getSize($file), exists($file), delete($file), copy($source, $dest). - Bài 2: Tạo class
Routervới static properties $routes và methods: get($path, $callback), post($path, $callback), dispatch($method, $path). - Bài 3: Tạo class
Logger(Singleton) với methods: debug($msg), info($msg), warning($msg), error($msg). Log vào file với timestamp.
Gợi ý Bài 1:
<?php
class FileHelper {
public static function getExtension($filename) {
return pathinfo($filename, PATHINFO_EXTENSION);
}
public static function getSize($file) {
if (!file_exists($file)) {
return false;
}
return filesize($file);
}
public static function exists($file) {
return file_exists($file);
}
public static function delete($file) {
if (self::exists($file)) {
return unlink($file);
}
return false;
}
public static function copy($source, $dest) {
if (!self::exists($source)) {
return false;
}
return copy($source, $dest);
}
public static function formatSize($bytes) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return round($bytes, 2) . ' ' . $units[$i];
}
}
// Sử dụng
echo FileHelper::getExtension("photo.jpg"); // jpg
$size = FileHelper::getSize("document.pdf");
echo FileHelper::formatSize($size); // 2.5 MB
if (FileHelper::exists("old.txt")) {
FileHelper::copy("old.txt", "new.txt");
FileHelper::delete("old.txt");
}
?>Gợi ý Bài 2:
<?php
class Router {
private static $routes = [];
public static function get($path, $callback) {
self::$routes['GET'][$path] = $callback;
}
public static function post($path, $callback) {
self::$routes['POST'][$path] = $callback;
}
public static function dispatch($method, $path) {
if (isset(self::$routes[$method][$path])) {
$callback = self::$routes[$method][$path];
return call_user_func($callback);
}
echo "404 - Route not found";
}
public static function getRoutes() {
return self::$routes;
}
}
// Định nghĩa routes
Router::get('/', function() {
echo "Home Page";
});
Router::get('/about', function() {
echo "About Page";
});
Router::post('/contact', function() {
echo "Contact Form Submitted";
});
// Dispatch
Router::dispatch('GET', '/'); // Home Page
Router::dispatch('GET', '/about'); // About Page
Router::dispatch('POST', '/contact'); // Contact Form Submitted
?>