أساسيات PHP
الأعضاء الثابتة والثوابت
فهم الأعضاء الثابتة
الخصائص والدوال الثابتة تنتمي إلى الصنف نفسه، وليس إلى كائنات فردية. يمكن الوصول إليها دون إنشاء مثيل من الصنف.
المفاهيم الأساسية:
- الخاصية الثابتة: متغير مشترك بين جميع مثيلات الصنف
- الدالة الثابتة: وظيفة يمكن استدعاؤها دون إنشاء كائن
- الوصول: استخدم عامل
::(عامل دقة النطاق) - الكلمة المفتاحية self: تشير إلى الصنف الحالي في السياق الثابت
الخصائص الثابتة
الخصائص الثابتة مشتركة عبر جميع مثيلات الصنف:
<?php
class Counter {
public static $count = 0; // خاصية ثابتة
private $id;
public function __construct() {
self::$count++; // زيادة الخاصية الثابتة
$this->id = self::$count;
}
public function getId() {
return $this->id;
}
public static function getCount() {
return self::$count;
}
}
// الوصول إلى الخاصية الثابتة دون إنشاء كائن
echo Counter::$count; // النتيجة: 0
// إنشاء كائنات
$obj1 = new Counter();
$obj2 = new Counter();
$obj3 = new Counter();
// التحقق من الخاصية الثابتة
echo Counter::$count; // النتيجة: 3
echo Counter::getCount(); // النتيجة: 3
echo $obj1->getId(); // النتيجة: 1
echo $obj2->getId(); // النتيجة: 2
echo $obj3->getId(); // النتيجة: 3
?>
مهم: استخدم
self::$property للوصول إلى الخصائص الثابتة داخل الصنف، و ClassName::$property خارج الصنف.
الدوال الثابتة
يمكن استدعاء الدوال الثابتة دون إنشاء مثيل:
<?php
class MathHelper {
// دوال ثابتة للعمليات الرياضية
public static function add($a, $b) {
return $a + $b;
}
public static function multiply($a, $b) {
return $a * $b;
}
public static function power($base, $exponent) {
return pow($base, $exponent);
}
public static function average(...$numbers) {
if (empty($numbers)) {
return 0;
}
return array_sum($numbers) / count($numbers);
}
}
// استدعاء الدوال الثابتة دون إنشاء كائن
echo MathHelper::add(5, 3); // النتيجة: 8
echo MathHelper::multiply(4, 7); // النتيجة: 28
echo MathHelper::power(2, 10); // النتيجة: 1024
echo MathHelper::average(10, 20, 30); // النتيجة: 20
?>
مثال عملي: اتصال قاعدة البيانات
تُستخدم الأعضاء الثابتة عادة لأنماط الوحدة الواحدة والموارد المشتركة:
<?php
class Database {
private static $instance = null;
private static $connectionCount = 0;
private $connection;
// البناء الخاص يمنع الإنشاء المباشر
private function __construct() {
self::$connectionCount++;
echo "اتصال قاعدة البيانات #{self::$connectionCount} تم إنشاؤه\n";
// محاكاة اتصال قاعدة البيانات
$this->connection = "اتصال MySQL";
}
// الحصول على مثيل واحد (نمط الوحدة الواحدة)
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public static function getConnectionCount() {
return self::$connectionCount;
}
public function query($sql) {
return "تنفيذ: {$sql} على {$this->connection}";
}
// منع الاستنساخ
private function __clone() {}
// منع إلغاء التسلسل
private function __wakeup() {}
}
// الحصول على مثيل قاعدة البيانات
$db1 = Database::getInstance(); // ينشئ الاتصال #1
$db2 = Database::getInstance(); // يرجع نفس المثيل
$db3 = Database::getInstance(); // يرجع نفس المثيل
echo Database::getConnectionCount(); // النتيجة: 1 (اتصال واحد فقط تم إنشاؤه)
echo $db1->query("SELECT * FROM users");
// جميع المتغيرات تشير إلى نفس المثيل
var_dump($db1 === $db2); // true
var_dump($db2 === $db3); // true
?>
نمط الوحدة الواحدة: يضمن أن يكون للصنف مثيل واحد فقط ويوفر نقطة وصول عامة إليه. مفيد لاتصالات قواعد البيانات ومديري التكوين والمسجلات.
مثال واقعي: مصادقة المستخدم
لننشئ نظام مصادقة شامل باستخدام الأعضاء الثابتة:
<?php
class Auth {
private static $currentUser = null;
private static $loginAttempts = [];
private static $maxAttempts = 5;
private static $lockoutTime = 900; // 15 دقيقة بالثواني
// التحقق من تسجيل دخول المستخدم
public static function isLoggedIn() {
return self::$currentUser !== null;
}
// الحصول على المستخدم الحالي
public static function getCurrentUser() {
return self::$currentUser;
}
// دالة تسجيل الدخول
public static function login($username, $password) {
// التحقق من قفل IP
$ip = self::getClientIP();
if (self::isLockedOut($ip)) {
return [
"success" => false,
"message" => "محاولات فاشلة كثيرة جداً. حاول مرة أخرى لاحقاً."
];
}
// التحقق من الاعتمادات (مبسط)
if (self::validateCredentials($username, $password)) {
self::$currentUser = [
"username" => $username,
"login_time" => time(),
"ip" => $ip
];
self::clearAttempts($ip);
return [
"success" => true,
"message" => "تسجيل دخول ناجح"
];
}
// تسجيل محاولة فاشلة
self::recordAttempt($ip);
return [
"success" => false,
"message" => "اعتمادات غير صالحة"
];
}
// دالة تسجيل الخروج
public static function logout() {
self::$currentUser = null;
}
// طلب المصادقة
public static function requireAuth() {
if (!self::isLoggedIn()) {
throw new Exception("المصادقة مطلوبة");
}
}
// التحقق من انتهاء صلاحية الجلسة
public static function isSessionExpired($maxAge = 3600) {
if (!self::isLoggedIn()) {
return true;
}
return (time() - self::$currentUser["login_time"]) > $maxAge;
}
// دوال مساعدة خاصة
private static function validateCredentials($username, $password) {
// التحقق المبسط
$validUsers = [
"admin" => "admin123",
"user" => "password123"
];
return isset($validUsers[$username]) && $validUsers[$username] === $password;
}
private static function getClientIP() {
return $_SERVER["REMOTE_ADDR"] ?? "127.0.0.1";
}
private static function isLockedOut($ip) {
if (!isset(self::$loginAttempts[$ip])) {
return false;
}
$attempts = self::$loginAttempts[$ip];
if (count($attempts) < self::$maxAttempts) {
return false;
}
$lastAttempt = end($attempts);
return (time() - $lastAttempt) < self::$lockoutTime;
}
private static function recordAttempt($ip) {
if (!isset(self::$loginAttempts[$ip])) {
self::$loginAttempts[$ip] = [];
}
self::$loginAttempts[$ip][] = time();
}
private static function clearAttempts($ip) {
if (isset(self::$loginAttempts[$ip])) {
unset(self::$loginAttempts[$ip]);
}
}
public static function getAttemptCount($ip) {
return isset(self::$loginAttempts[$ip]) ? count(self::$loginAttempts[$ip]) : 0;
}
}
// مثال الاستخدام
if (!Auth::isLoggedIn()) {
$result = Auth::login("admin", "admin123");
if ($result["success"]) {
echo "مرحباً، " . Auth::getCurrentUser()["username"];
} else {
echo "فشل تسجيل الدخول: " . $result["message"];
}
}
// حماية صفحة
try {
Auth::requireAuth();
echo "هذه صفحة محمية";
} catch (Exception $e) {
echo "الوصول مرفوض: " . $e->getMessage();
}
// التحقق من انتهاء صلاحية الجلسة
if (Auth::isSessionExpired(1800)) { // 30 دقيقة
Auth::logout();
echo "انتهت صلاحية الجلسة، يرجى تسجيل الدخول مرة أخرى";
}
?>
ثوابت الصنف
الثوابت هي قيم لا يمكن تغييرها بمجرد تعريفها:
<?php
class Configuration {
// تعريف الثوابت
public const APP_NAME = "تطبيقي";
public const VERSION = "1.0.0";
public const MAX_UPLOAD_SIZE = 5242880; // 5MB بالبايت
private const API_SECRET = "مفتاح-سري-123";
// يمكن استخدام الثوابت في الحسابات
public const UPLOAD_SIZE_MB = self::MAX_UPLOAD_SIZE / 1024 / 1024;
public static function getConfig($key) {
$constants = [
"app_name" => self::APP_NAME,
"version" => self::VERSION,
"max_upload" => self::MAX_UPLOAD_SIZE
];
return $constants[$key] ?? null;
}
public static function validateUploadSize($fileSize) {
return $fileSize <= self::MAX_UPLOAD_SIZE;
}
// ثابت خاص يمكن الوصول إليه فقط داخل الصنف
public static function getApiUrl() {
return "https://api.example.com/" . self::API_SECRET;
}
}
// الوصول إلى الثوابت
echo Configuration::APP_NAME; // النتيجة: تطبيقي
echo Configuration::VERSION; // النتيجة: 1.0.0
echo Configuration::UPLOAD_SIZE_MB; // النتيجة: 5
// الاستخدام في الدوال
$fileSize = 6000000; // 6MB
if (Configuration::validateUploadSize($fileSize)) {
echo "حجم الملف مقبول";
} else {
echo "الملف كبير جداً. الحد الأقصى: " . Configuration::UPLOAD_SIZE_MB . "MB";
}
?>
الثوابت مقابل الخصائص الثابتة:
- الثوابت: لا يمكن تغييرها، استخدم الكلمة المفتاحية
const - الخصائص الثابتة: يمكن تعديلها، استخدم الكلمة المفتاحية
static
التعدادات مع الثوابت
إنشاء هياكل تشبه التعداد باستخدام الثوابت:
<?php
class OrderStatus {
public const PENDING = "pending";
public const PROCESSING = "processing";
public const SHIPPED = "shipped";
public const DELIVERED = "delivered";
public const CANCELLED = "cancelled";
public static function isValid($status) {
$validStatuses = [
self::PENDING,
self::PROCESSING,
self::SHIPPED,
self::DELIVERED,
self::CANCELLED
];
return in_array($status, $validStatuses);
}
public static function getAll() {
return [
self::PENDING,
self::PROCESSING,
self::SHIPPED,
self::DELIVERED,
self::CANCELLED
];
}
public static function getLabel($status) {
$labels = [
self::PENDING => "قيد الانتظار",
self::PROCESSING => "قيد المعالجة",
self::SHIPPED => "تم الشحن",
self::DELIVERED => "تم التسليم",
self::CANCELLED => "ملغي"
];
return $labels[$status] ?? "غير معروف";
}
}
class Order {
private $id;
private $status;
private $items;
public function __construct($id) {
$this->id = $id;
$this->status = OrderStatus::PENDING;
$this->items = [];
}
public function setStatus($status) {
if (OrderStatus::isValid($status)) {
$this->status = $status;
return true;
}
return false;
}
public function getStatus() {
return $this->status;
}
public function getStatusLabel() {
return OrderStatus::getLabel($this->status);
}
public function canBeCancelled() {
return in_array($this->status, [
OrderStatus::PENDING,
OrderStatus::PROCESSING
]);
}
}
// الاستخدام
$order = new Order(12345);
echo $order->getStatusLabel(); // النتيجة: قيد الانتظار
$order->setStatus(OrderStatus::PROCESSING);
echo $order->getStatusLabel(); // النتيجة: قيد المعالجة
if ($order->canBeCancelled()) {
$order->setStatus(OrderStatus::CANCELLED);
echo "تم إلغاء الطلب";
}
// سرد جميع الحالات
foreach (OrderStatus::getAll() as $status) {
echo OrderStatus::getLabel($status) . "\n";
}
?>
الربط الثابت المتأخر
استخدم static:: بدلاً من self:: للربط الثابت المتأخر في الوراثة:
<?php
class Animal {
protected static $species = "غير معروف";
public static function getSpecies() {
return self::$species; // ربط مبكر - يرجع دائماً "غير معروف"
}
public static function getSpeciesLate() {
return static::$species; // ربط متأخر - يرجع قيمة الصنف الابن
}
public static function create() {
return new static(); // ينشئ مثيلاً من الصنف المستدعى
}
}
class Dog extends Animal {
protected static $species = "Canis familiaris";
}
class Cat extends Animal {
protected static $species = "Felis catus";
}
// ربط مبكر (self::)
echo Dog::getSpecies(); // النتيجة: غير معروف (يستخدم قيمة الأب)
echo Cat::getSpecies(); // النتيجة: غير معروف (يستخدم قيمة الأب)
// ربط ثابت متأخر (static::)
echo Dog::getSpeciesLate(); // النتيجة: Canis familiaris
echo Cat::getSpeciesLate(); // النتيجة: Felis catus
// إنشاء مثيلات باستخدام الربط الثابت المتأخر
$dog = Dog::create(); // ينشئ مثيل Dog
$cat = Cat::create(); // ينشئ مثيل Cat
echo get_class($dog); // النتيجة: Dog
echo get_class($cat); // النتيجة: Cat
?>
self مقابل static:
- self:: يشير إلى الصنف حيث كُتب (ربط مبكر)
- static:: يشير إلى الصنف الذي تم استدعاؤه (ربط متأخر)
- استخدم static:: عندما تريد أن تستخدم الأصناف الأبناء قيمها الخاصة
مثال عملي: نظام التسجيل
نظام تسجيل شامل باستخدام الأعضاء الثابتة:
<?php
class Logger {
// مستويات التسجيل كثوابت
public const DEBUG = "DEBUG";
public const INFO = "INFO";
public const WARNING = "WARNING";
public const ERROR = "ERROR";
public const CRITICAL = "CRITICAL";
private static $logs = [];
private static $logLevel = self::INFO;
private static $logToFile = false;
private static $logFile = "app.log";
public static function setLogLevel($level) {
self::$logLevel = $level;
}
public static function enableFileLogging($filename = "app.log") {
self::$logToFile = true;
self::$logFile = $filename;
}
public static function debug($message, $context = []) {
self::log(self::DEBUG, $message, $context);
}
public static function info($message, $context = []) {
self::log(self::INFO, $message, $context);
}
public static function warning($message, $context = []) {
self::log(self::WARNING, $message, $context);
}
public static function error($message, $context = []) {
self::log(self::ERROR, $message, $context);
}
public static function critical($message, $context = []) {
self::log(self::CRITICAL, $message, $context);
}
private static function log($level, $message, $context) {
// التحقق من أن المستوى مرتفع بما يكفي للتسجيل
$levels = [
self::DEBUG => 1,
self::INFO => 2,
self::WARNING => 3,
self::ERROR => 4,
self::CRITICAL => 5
];
if ($levels[$level] < $levels[self::$logLevel]) {
return;
}
$logEntry = [
"timestamp" => date("Y-m-d H:i:s"),
"level" => $level,
"message" => $message,
"context" => $context
];
self::$logs[] = $logEntry;
// الكتابة إلى ملف إذا كان ممكناً
if (self::$logToFile) {
self::writeToFile($logEntry);
}
}
private static function writeToFile($logEntry) {
$line = sprintf(
"[%s] %s: %s %s\n",
$logEntry["timestamp"],
$logEntry["level"],
$logEntry["message"],
!empty($logEntry["context"]) ? json_encode($logEntry["context"]) : ""
);
file_put_contents(self::$logFile, $line, FILE_APPEND);
}
public static function getLogs($level = null) {
if ($level === null) {
return self::$logs;
}
return array_filter(self::$logs, function($log) use ($level) {
return $log["level"] === $level;
});
}
public static function clear() {
self::$logs = [];
}
public static function getStats() {
$stats = [
self::DEBUG => 0,
self::INFO => 0,
self::WARNING => 0,
self::ERROR => 0,
self::CRITICAL => 0
];
foreach (self::$logs as $log) {
$stats[$log["level"]]++;
}
return $stats;
}
}
// الاستخدام
Logger::setLogLevel(Logger::DEBUG);
Logger::enableFileLogging("myapp.log");
Logger::debug("بدأ التطبيق");
Logger::info("سجل المستخدم دخوله", ["user_id" => 123]);
Logger::warning("استخدام ذاكرة عالي", ["memory" => "85%"]);
Logger::error("فشل اتصال قاعدة البيانات", ["host" => "localhost"]);
Logger::critical("تعطل النظام!", ["error" => "نفاد الذاكرة"]);
// الحصول على جميع السجلات
$allLogs = Logger::getLogs();
echo "إجمالي السجلات: " . count($allLogs) . "\n";
// الحصول على الأخطاء فقط
$errors = Logger::getLogs(Logger::ERROR);
echo "الأخطاء: " . count($errors) . "\n";
// الحصول على الإحصائيات
$stats = Logger::getStats();
print_r($stats);
?>
تمرين:
أنشئ صنف Cache مع أعضاء ثابتة:
- خاصية ثابتة خاصة
$cache(مصفوفة لتخزين البيانات المخزنة) - دالة ثابتة
set($key, $value, $ttl)- تخزين قيمة مع وقت العيش - دالة ثابتة
get($key)- استرجاع قيمة إذا لم تنته صلاحيتها - دالة ثابتة
has($key)- التحقق من وجود المفتاح وعدم انتهاء صلاحيته - دالة ثابتة
delete($key)- إزالة عنصر مخزن - دالة ثابتة
clear()- مسح جميع التخزين المؤقت - دالة ثابتة
getStats()- إرجاع النجاحات والإخفاقات وإجمالي العناصر - ثوابت لقيم TTL الشائعة (ساعة، يوم، أسبوع)
أفضل الممارسات
إرشادات مهمة:
- استخدم static باعتدال: الكثير من الأعضاء الثابتة يمكن أن تجعل الاختبار صعباً
- استخدم للدوال المساعدة: مساعدات الرياضيات، المنسقات، المدققات
- استخدم للموارد المشتركة: اتصالات قواعد البيانات، التكوين
- ثوابت للقيم الثابتة: استخدم const للقيم التي لا تتغير أبداً
- الربط الثابت المتأخر: استخدم static:: عند العمل مع الوراثة
- تجنب الحالة الثابتة: الخصائص الثابتة تنشئ حالة عامة، استخدمها بحذر
الملخص
في هذا الدرس، تعلمت:
- الخصائص والدوال الثابتة التي تنتمي إلى الصنف، وليس المثيلات
- كيفية استخدام عامل
::للوصول الثابت - الفرق بين
self::وstatic:: - ثوابت الصنف مع الكلمة المفتاحية
const - تنفيذ نمط الوحدة الواحدة
- الاستخدامات العملية مثل المصادقة والتسجيل والتكوين
- أفضل الممارسات للأعضاء الثابتة
بعد ذلك، سنستكشف السمات والدوال السحرية لإعادة استخدام الكود والسلوكيات الخاصة للأصناف!