التابع أو الخاصية static ملك للصنف نفسه لا لأي كائن. تستدعي التابع الساكن بـ ClassName::method() وتقرأ الخاصية الساكنة بـ ClassName::$property، دون إنشاء كائن أولًا. وداخل الصنف، تشير self:: إلى الأعضاء أنفسهم.
لا توجد new Temperature() في أي مكان. والأعضاء الساكنة مفيدة للدوال المساعدة التي لا تحتاج إلى حالة كائن (التحويلات، والمصانع، والتحقق) وللبيانات التي تخص الصنف كله، مثل عداد.
الخصائص الساكنة يتشاركها كل الكائنات
للخاصية العادية قيمة لكل كائن. وللخاصية الساكنة قيمة واحدة للصنف كله، فيرى كل كائن القيمة نفسها. هنا يزيد كل User جديد عدادًا مشتركًا ويحصل على المعرّف التالي:
لاحظ $ في self::$nextId: تحتفظ الخصائص الساكنة بها بعد ::، بينما لا تحتاجها الثوابت (self::MAX). وتعيش الخاصية الساكنة طوال طلب PHP واحد فقط. في موقع ويب نموذجي (PHP-FPM أو Apache) تبدأ من جديد مع كل تحميل صفحة، لذا ليست مكانًا لتخزين البيانات بين الزوار.
التوابع الساكنة ليس فيها $this
يعمل التابع الساكن دون كائن، لذا لا توجد $this داخله. يستطيع التابع الساكن استدعاء توابع ساكنة أخرى بـ self::، لكنه لا يستطيع قراءة الخصائص العادية. واستدعاء تابع عادي استدعاءً ساكنًا يفشل أيضًا:
الاتجاه المعاكس لا مشكلة فيه: يستطيع التابع العادي استدعاء التوابع الساكنة، كما تفعل withSeparator() مع self::make().
self مقابل static: الربط الساكن المتأخر
تشير self:: إلى الصنف الذي كُتبت فيه الشيفرة. وتشير static:: إلى الصنف الذي استُدعي عليه التابع وقت التشغيل. ولا يظهر الفرق إلا مع الوراثة، وأهم ما يكون مع توابع المصنع:
تعيد Post::createWithSelf() كائن Model، وهذا ليس ما أراده المستدعي في الغالب. أما static فتُحل إلى Post لأنه الصنف المذكور في الاستدعاء؛ وتسمي PHP هذا الربط الساكن المتأخر. استخدم static:: وnew static() في التوابع المقصود وراثتها، وself:: عندما تقصد فعلًا هذا الصنف بالضبط. والقاعدة نفسها تنطبق على الثوابت: تلتقط static::TABLE الثابت TABLE الخاص بالابن، بينما تقرأ self::TABLE دائمًا الثابت الموجود في الصنف الذي كُتبت فيه الشيفرة.
الخصائص الساكنة الموروثة مشتركة
يتشارك الصنف الابن الخاصية الساكنة لأبيه ما لم يصرّح بخاصيته الخاصة. وهذه مفاجأة static الكلاسيكية: صنفان فرعيان يعدّان في رقم واحد.
ليس لـ Apples خاصية $count خاصة بها، لذا فإن Apples::$count هي Counter::$count، ويطبع الاثنان 2. أما Pears فتعيد التصريح بالخاصية وتعدّ وحدها. والخاصية الساكنة المصرّح بها في سمة تتصرف بشكل مختلف: كل صنف يستخدم السمة يحصل على نسخة منفصلة.
المتغيرات الساكنة داخل الدوال
لـ static معنى ثانٍ داخل جسم الدالة: يحتفظ المتغير الساكن بقيمته بين الاستدعاءات. ولا تُسند القيمة الابتدائية إلا في أول مرة تعمل فيها الدالة:
يتخطى استدعاء slowSquare(12) الثاني الحساب. ومثل الخصائص الساكنة، تُعاد هذه القيم في نهاية كل طلب. وتشرح صفحة المتغيرات نطاق المتغيرات عمومًا.
متى تكون static الخيار الخاطئ
التوابع الساكنة مريحة، وهذا يجعل الإفراط فيها سهلًا. الصنف الذي تقرأ كل توابعه خصائص ساكنة وتكتبها هو حالة عامة عليها اسم صنف: يتشاركها كل مستدعٍ، ولا تستطيع الاختبارات الحصول على نسخة جديدة، ولا يمكنك تبديلها بتنفيذ مختلف.
الاستخدامات الجيدة لـ static: الدوال المساعدة الخالصة التي لا حالة لها (Slug::make())، والبواني المسماة (Color::fromHex())، والثوابت على مستوى الصنف. وإذا احتاج تابع إلى بيانات قد تختلف بين استخدامين، فهو يريد كائنًا.
الأسئلة الشائعة
كيف أستدعي تابعًا ساكنًا في PHP؟
باسم الصنف و::: MathHelper::square(4). وداخل الصنف نفسه استخدم self::square(4) أو static::square(4). لا حاجة إلى كائن، وداخل التابع الساكن لا توجد $this.
ما الفرق بين self وstatic في PHP؟
تعني self:: دائمًا الصنف الذي كُتبت فيه الشيفرة. وتعني static:: الصنف الذي استُدعي عليه التابع فعلًا، وقد يكون صنفًا ابنًا؛ ويُسمى هذا الربط الساكن المتأخر. لذا في تابع create() في الأب، تبني new self() الأب دائمًا، بينما تبني new static() أي ابن استدعاه.
ما هي الخاصية الساكنة في PHP؟
الخاصية المصرّح بها public static int $count = 0; ملك للصنف، فتوجد قيمة واحدة يتشاركها كل كائن من ذلك الصنف (وأبناؤه، ما لم يعِد ابن التصريح بها). اقرأها واكتبها بـ ClassName::$count أو self::$count؛ لاحظ علامة $.
هل أستطيع استدعاء تابع ساكن على كائن في PHP؟
نعم. تستدعي $obj->square(4) و$obj::square(4) كلتاهما التابع الساكن لصنف الكائن، لكن لا توجد $this داخله مع ذلك. أما استدعاء تابع غير ساكن على شكل ClassName::method() فهو الاتجاه الذي يفشل: منذ PHP 8 يرمي Error.
هل أستطيع استخدام $this في تابع ساكن؟
لا. يعمل التابع الساكن دون كائن، لذا $this غير معرّفة هناك واستخدامها يرمي Error ("Using $this when not in object context"). استخدم self:: أو static:: للوصول إلى الأعضاء الساكنة الأخرى.