أساسيات PHP

معدلات الوصول والتغليف

13 دقيقة الدرس 22 من 45

فهم معدلات الوصول

معدلات الوصول تتحكم في رؤية وإمكانية الوصول للخصائص والدوال في أصنافك. إنها أساسية للتغليف، أحد المبادئ الأساسية للـ OOP.

ثلاثة معدلات وصول في PHP:
  • public: يمكن الوصول إليها من أي مكان (داخل الصنف، خارج الصنف، الأصناف الفرعية)
  • protected: يمكن الوصول إليها فقط داخل الصنف والأصناف الفرعية
  • private: يمكن الوصول إليها فقط داخل الصنف نفسه

معدل الوصول العام (Public)

يمكن الوصول إلى الخصائص والدوال العامة من أي مكان:

<?php class User { public $name; // يمكن الوصول إليها في أي مكان public $email; // يمكن الوصول إليها في أي مكان public function __construct($name, $email) { $this->name = $name; $this->email = $email; } public function greet() { // يمكن استدعاؤها في أي مكان return "مرحباً، {$this->name}!"; } } $user = new User("أحمد", "ahmed@example.com"); echo $user->name; // يعمل بشكل جيد echo $user->greet(); // يعمل بشكل جيد $user->name = "علي"; // يمكن التعديل مباشرة ?>

معدل الوصول الخاص (Private)

يمكن الوصول إلى الخصائص والدوال الخاصة فقط من داخل الصنف:

<?php class BankAccount { private $balance; // لا يمكن الوصول من الخارج private $accountNumber; // لا يمكن الوصول من الخارج public function __construct($accountNumber, $initialBalance) { $this->accountNumber = $accountNumber; $this->balance = $initialBalance; } // دالة عامة للوصول إلى خاصية خاصة public function getBalance() { return $this->balance; } public function deposit($amount) { if ($amount > 0) { $this->balance += $amount; $this->logTransaction("إيداع", $amount); // استدعاء دالة خاصة return true; } return false; } public function withdraw($amount) { if ($amount > 0 && $amount <= $this->balance) { $this->balance -= $amount; $this->logTransaction("سحب", $amount); return true; } return false; } // دالة خاصة - يمكن استدعاؤها فقط داخل هذا الصنف private function logTransaction($type, $amount) { echo "معاملة: {$type} بقيمة \${$amount} على الحساب {$this->accountNumber}\n"; } } $account = new BankAccount("123456", 1000); echo $account->getBalance(); // يعمل: 1000 $account->deposit(500); // يعمل // هذه ستسبب أخطاء: // echo $account->balance; // خطأ: لا يمكن الوصول إلى خاصية خاصة // echo $account->accountNumber; // خطأ: لا يمكن الوصول إلى خاصية خاصة // $account->logTransaction("اختبار", 100); // خطأ: لا يمكن استدعاء دالة خاصة ?>
لماذا نستخدم Private؟ الخصائص الخاصة تحمي البيانات من التعديل بشكل غير صحيح. أنت تتحكم في الوصول من خلال الدوال العامة، مما يضمن سلامة البيانات.

معدل الوصول المحمي (Protected)

يمكن الوصول إلى الخصائص والدوال المحمية داخل الصنف وأصنافه الفرعية:

<?php class Animal { protected $name; // يمكن الوصول إليها في هذا الصنف والأصناف الفرعية protected $age; private $id; // يمكن الوصول إليها فقط في هذا الصنف public function __construct($name, $age) { $this->name = $name; $this->age = $age; $this->id = uniqid(); } protected function makeSound() { // يمكن استخدامها من قبل الأصناف الفرعية return "صوت حيوان عام"; } public function introduce() { return "{$this->name} عمره {$this->age} سنوات"; } } class Dog extends Animal { public function bark() { // يمكن الوصول إلى الخصائص المحمية من الأب return "{$this->name} يقول: عواء! عواء!"; } public function showInfo() { // يمكن استدعاء الدالة المحمية من الأب return $this->makeSound(); } public function showId() { // لا يمكن الوصول إلى الخاصية الخاصة من الأب // return $this->id; // هذا سيسبب خطأ } } $dog = new Dog("ماكس", 3); echo $dog->bark(); // يعمل بشكل جيد echo $dog->introduce(); // يعمل بشكل جيد // هذه ستسبب أخطاء: // echo $dog->name; // خطأ: لا يمكن الوصول إلى خاصية محمية // echo $dog->makeSound(); // خطأ: لا يمكن استدعاء دالة محمية ?>

شرح التغليف

التغليف هو ممارسة إخفاء البيانات الداخلية وتوفير وصول محكوم من خلال الدوال:

<?php class Product { private $name; private $price; private $discount = 0; public function __construct($name, $price) { $this->name = $name; $this->setPrice($price); // استخدام setter للتحقق } // دوال القراءة (getters) public function getName() { return $this->name; } public function getPrice() { return $this->price; } public function getDiscount() { return $this->discount; } public function getFinalPrice() { return $this->price - ($this->price * $this->discount / 100); } // دوال الكتابة (setters) مع التحقق public function setName($name) { if (strlen($name) >= 3) { $this->name = $name; return true; } return false; } public function setPrice($price) { if ($price > 0) { $this->price = $price; return true; } return false; } public function setDiscount($discount) { if ($discount >= 0 && $discount <= 100) { $this->discount = $discount; return true; } return false; } } $product = new Product("لابتوب", 999); echo $product->getName(); // لابتوب echo $product->getPrice(); // 999 $product->setDiscount(10); // صحيح: خصم 10% echo $product->getFinalPrice(); // 899.1 $product->setDiscount(150); // غير صحيح: يُتجاهل $product->setPrice(-100); // غير صحيح: يُتجاهل ?>
فوائد التغليف:
  • التحقق من البيانات: التحكم في القيم التي يتم تعيينها
  • الأمان: إخفاء البيانات الحساسة
  • المرونة: تغيير التنفيذ الداخلي دون التأثير على الكود الخارجي
  • قابلية الصيانة: أسهل في التصحيح والتعديل

مثال واقعي: مصادقة المستخدم

إليك مثالاً عملياً باستخدام التغليف الصحيح:

<?php class User { private $username; private $email; private $passwordHash; private $isActive = true; private $loginAttempts = 0; private $maxLoginAttempts = 3; public function __construct($username, $email, $password) { $this->setUsername($username); $this->setEmail($email); $this->setPassword($password); } // دوال القراءة public function getUsername() { return $this->username; } public function getEmail() { return $this->email; } public function isActive() { return $this->isActive; } public function getLoginAttempts() { return $this->loginAttempts; } // دوال الكتابة مع التحقق public function setUsername($username) { if (strlen($username) >= 3 && strlen($username) <= 20) { $this->username = $username; return true; } throw new Exception("اسم المستخدم يجب أن يكون بين 3 و 20 حرفاً"); } public function setEmail($email) { if (filter_var($email, FILTER_VALIDATE_EMAIL)) { $this->email = $email; return true; } throw new Exception("صيغة البريد الإلكتروني غير صالحة"); } private function setPassword($password) { if (strlen($password) >= 8) { $this->passwordHash = password_hash($password, PASSWORD_DEFAULT); return true; } throw new Exception("كلمة المرور يجب أن تكون 8 أحرف على الأقل"); } // دوال المصادقة public function verifyPassword($password) { if (!$this->isActive) { return ["success" => false, "message" => "الحساب مقفل"]; } if (password_verify($password, $this->passwordHash)) { $this->loginAttempts = 0; // إعادة تعيين المحاولات return ["success" => true, "message" => "تسجيل دخول ناجح"]; } $this->loginAttempts++; if ($this->loginAttempts >= $this->maxLoginAttempts) { $this->isActive = false; return ["success" => false, "message" => "تم قفل الحساب بسبب محاولات فاشلة كثيرة"]; } return ["success" => false, "message" => "كلمة مرور غير صحيحة"]; } public function changePassword($oldPassword, $newPassword) { if (password_verify($oldPassword, $this->passwordHash)) { $this->setPassword($newPassword); return "تم تغيير كلمة المرور بنجاح"; } return "كلمة المرور الحالية غير صحيحة"; } public function resetLoginAttempts() { $this->loginAttempts = 0; $this->isActive = true; } } // الاستخدام try { $user = new User("ahmed123", "ahmed@example.com", "SecurePass123"); // محاولة تسجيل دخول بكلمة مرور خاطئة $result = $user->verifyPassword("wrongpass"); echo $result["message"] . "\n"; $result = $user->verifyPassword("wrongpass"); echo $result["message"] . "\n"; $result = $user->verifyPassword("wrongpass"); echo $result["message"] . "\n"; // تم قفل الحساب // محاولة بكلمة مرور صحيحة (ستفشل - الحساب مقفل) $result = $user->verifyPassword("SecurePass123"); echo $result["message"] . "\n"; // المسؤول يعيد تعيين المحاولات $user->resetLoginAttempts(); // الآن يمكن تسجيل الدخول $result = $user->verifyPassword("SecurePass123"); echo $result["message"] . "\n"; // نجح } catch (Exception $e) { echo "خطأ: " . $e->getMessage(); } ?>

ترقية الخصائص (PHP 8+)

قدمت PHP 8 ترقية خصائص البناء، صيغة أقصر:

<?php // الطريقة التقليدية class Product { private $name; private $price; private $category; public function __construct($name, $price, $category) { $this->name = $name; $this->price = $price; $this->category = $category; } } // طريقة PHP 8+ (ترقية الخصائص) class Product { public function __construct( private string $name, private float $price, private string $category ) {} public function getName(): string { return $this->name; } public function getPrice(): float { return $this->price; } } $product = new Product("لابتوب", 999.99, "إلكترونيات"); echo $product->getName(); // لابتوب ?>
ترقية الخصائص: تجمع بين تصريح المعامل وتعيين الخاصية في سطر واحد. أنظف وأكثر إيجازاً!

الخصائص للقراءة فقط (PHP 8.1+)

يمكن تهيئة الخصائص للقراءة فقط مرة واحدة ولا يمكن تعديلها:

<?php class Configuration { public function __construct( public readonly string $appName, public readonly string $version, public readonly bool $debug ) {} } $config = new Configuration("تطبيقي", "1.0.0", true); echo $config->appName; // تطبيقي // هذا سيسبب خطأ: // $config->appName = "اسم جديد"; // خطأ: لا يمكن تعديل خاصية للقراءة فقط ?>

متى تستخدم كل معدل

إرشادات:
  • استخدم private: للبيانات الداخلية التي لا يجب الوصول إليها مباشرة (كلمات المرور، المعرفات، الحسابات الداخلية)
  • استخدم protected: للبيانات التي تحتاج الأصناف الفرعية للوصول إليها
  • استخدم public: للدوال التي تشكل واجهة برمجة التطبيق العامة للصنف، نادراً للخصائص
  • افتراضياً إلى private: ابدأ بـ private واجعل الأشياء أكثر إمكانية للوصول فقط إذا لزم الأمر
تمرين:

أنشئ صنف ShoppingCart مع تغليف صحيح:

  • خاصية خاصة: items (مصفوفة)
  • خاصية خاصة: customerEmail
  • دالة: addItem($product, $quantity) - تحقق من الكمية > 0
  • دالة: removeItem($productName)
  • دالة: getTotal() - تحسب السعر الإجمالي
  • دالة: getItemCount() - تُرجع عدد العناصر الفريدة
  • دالة: setCustomerEmail($email) - تحقق من صيغة البريد
  • دالة: getItems() - تُرجع مصفوفة العناصر

اختبر بإضافة عناصر، حساب الإجمالي، ومعالجة المدخلات غير الصحيحة.

أفضل الممارسات

إرشادات مهمة:
  • مبدأ أقل امتياز: أعطِ الحد الأدنى من الوصول الضروري
  • لا تكشف البيانات الحساسة أبداً: احتفظ بكلمات المرور والرموز والمفاتيح خاصة
  • تحقق دائماً في setters: تحقق من البيانات قبل تعيين الخصائص
  • استخدم getters/setters: لا تصل إلى الخصائص مباشرة من الخارج
  • وثق واجهة برمجة التطبيق: اجعل من الواضح أي دوال يُقصد استخدامها

الملخص

في هذا الدرس، تعلمت:

  • معدلات الوصول الثلاثة: public, private, protected
  • متى ولماذا تستخدم كل معدل
  • ما هو التغليف ولماذا هو مهم
  • كيفية إنشاء getters و setters للتحقق من البيانات
  • ترقية الخصائص والخصائص للقراءة فقط (PHP 8+)
  • أفضل الممارسات لحماية البيانات

بعد ذلك، سنستكشف الوراثة والتعددية الشكلية لبناء تسلسلات أصناف أكثر مرونة!