اذهب إلى المحتوى

المتغيرات والثوابت في PHP


سارة محمد2

الوصول الديناميكي إلى المتغير عن طريق الاسم (المتغيرات المتغيرة)

يمكن الوصول إلى المتغيرات عبر أسماء المتغيرات الديناميكية. يمكن تخزين اسم المتغير في متغير آخر، مما يسمح بالوصول إليه بشكلٍ ديناميكي وتُعرف هذه المتغيرات باسم المتغيرات المتغيرة (variable variables).

لتغيير المتغير إلى متغير قابل للتغيير نضع إشارة $ إضافية أمام المتغير.

$variableName = 'foo';
$foo = 'bar';

// "bar" فيما يلي كل التعليمات متكافئة وكل الخرج سيكون 
echo $foo;
echo ${$variableName};
echo $$variableName;

// بشكلٍ مماثل
$variableName = 'foo';
$$variableName = 'bar';


// 'bar' التعليمات التالية ستنتج أيضًا الخرج  
echo $foo;
echo $$variableName;
echo ${$variableName};

المتغيرات المتغيرة مفيدة لتحديد استدعاءات الدالة:

function add($a, $b) {
    return $a + $b;
}

$funcName = 'add';

echo $funcName(1, 2); // outputs 3

سيكون هذا مفيدًا في أصناف (classes) لغة PHP:

class myClass {
    public function __construct() {
        $functionName = 'doSomething';
        $this->$functionName('Hello World');
    }

    private function doSomething($string) {
        echo $string; // Outputs "Hello World"
    }
}

من الممكن، ولكن ليس من الضروري وضع ‎$variableName بين {}:

${$variableName} = $value;

الأمثلة التالية متكافئة والخرج "baz":

$fooBar = 'baz';
$varPrefix = 'foo';

echo $fooBar; // "baz" الخرج 
echo ${$varPrefix . 'Bar'}; // "baz" الخرج أيضًا 

استخدام القوسين {} يكون إلزاميًا فقط عندما يكون اسم المتغير في حد ذاته تعبيرًا، مثل:

${$variableNamePart1 . $variableNamePart2} = $value;

ومع ذلك يُنصح دائمًا باستخدام القوسين {}، لأنهما أكثر قابلية للقراءة. على الرغم من أنه لا يوصى بذلك، إلا أنه من الممكن وضع سلسلة لهذا السلوك:

$$$$$$$$DoNotTryThisAtHomeKids = $value;

من المهم ملاحظة أنّ العديد من المطورين يعدون الاستخدام المفرط للمتغيرات المتغيرة ممارسةً سيئةً، لأنّها غير مناسبة تمامًا للتحليل الثابت من قِبل بيئات التطوير المتكاملة (IDE)، إذ يمكن أن تصبح الشيفرات الأساسية (codebases) الكبيرة مع الكثير من المتغيرات المتغيرة (أو استدعاءات التوابع الديناميكية) صعبة الصيانة.

الفروقات بين PHP5 وPHP7‎

السبب الآخر لاستخدام القوسين {} أو () دائمًا هو أنّ PHP5 وPHP7 يختلفان قليلًا في طريقة التعامل مع المتغيرات الديناميكية وقد يؤدي ذلك في بعض الحالات إلى خرجٍ مختلف. ‫ في PHP7، ستُعالج المتغيرات الديناميكية (dynamic variables) والخاصيات (properties) والتوابع (methods) بالترتيب من اليسار إلى اليمين (بما أن الشيفرة تُكتَب من اليسار إلى اليمين) على عكس PHP5 التي تتضمن العديد من الحالات الخاصة، تُظهر الأمثلة أدناه كيف يتغير ترتيب المعالجة

الحالة 1: ‎$$foo['bar']['baz']‎‎

  • ترجمة PHP5:
${$foo['bar']['baz']}
  • ترجمة PHP7:
($$foo)['bar']['baz']

الحالة 2: ‎$foo->$bar['baz']‎

  • ترجمة PHP5:
$foo->{$bar['baz']}
  • ترجمة PHP7:
($foo->$bar)['baz']

الحالة 3: ‎$foo->$bar['baz']()‎‎

  • ترجمة PHP5:
$foo->{$bar['baz']}()
  • ترجمة PHP7:
($foo->$bar)['baz']()

الحالة 4: Foo::$bar['baz']()‎

  • ترجمة PHP5:
Foo::{$bar['baz']}()
  • ترجمة PHP7:
(Foo::$bar)['baz']()

أنواع البيانات

يوجد أنواع مختلفة من البيانات في PHP لتحقيق الأغراض المختلفة، ولا نحتاج إلى تعريف صريح لنوع البيانات المحتواة في المتغير بل تعيّنه اللغة بالاعتماد على نوع القيمة التي أُسنِدت إليه أو على النوع الذي حُوِّل إليه. هذه نظرة عامة مختصرة عن الأنواع، راجع موضوع أنواع PHP للحصول على توثيق وأمثلة مفصلة.

يوجد في PHP أنواع البيانات التالية:

  • null
  • boolean: القيم المنطقية
  • integer: الأعداد الصحيحة
  • float: الأعداد العشرية
  • string: السلاسل النصية
  • object: الكائنات
  • resources: الموارد
  • array: المصفوفات

NULL

تُسنَد القيمة المعدومة Null إلى أي متغيّر وتمثل متغيرًا دون قيمة أو عديم القيمة.

$foo = null;

تُبطل null المتغير وترجع قيمة غير معرّف (undefined) أو void إذا استُدعيَ.

النوع boolean: القيم المنطقية

هذا أبسط نوع وله قيمتين ممكنتين فقط.

$foo = true;
$bar = false;

يمكن أن تُستخدم القيم المنطقية للتحكم بتدفق الشيفرة.

$foo = true;

if ($foo) {
    echo "true";
} else {
    echo "false";
}

النوع integer: الأعداد الصحيحة

العدد الصحيح هو عدد سالب أو موجب، يمكن استخدامه مع أيّ أساس عددي ويختلف حجمه حسب المنصة. لا تدعم PHP الأعداد الصحيحة دون إشارة (unsigned).

// عدد صحيح سالب
$foo = -3; 

// (‎كقيمة منطقية) false أو null الصفر يمكن أن يكون
$foo = 0; 

// عدد عشري موجب 
$foo = 123; 

// ‏عدد بالنظام الثماني = 83 بالنظام العشري
$bar = 0123; 

// ‏عدد بالنظام الست عشري = 171 بالنظام العشري
$bar = 0xAB; 

// عدد بالنظام الثنائي = 10 بالنظام العشري
$bar = 0b1010; 

var_dump(0123, 0xAB, 0b1010); 
// int(83), int(171), int(10) 

النوع float: الأعداد العشرية

الأعداد ذات الفاصلة العشرية التي تتمثل بالنوعين: "doubles" وهو عدد عشري مضاعف الدقة أو "floats" وهو عدد عشري عادي (دقة افتراضية).

$foo = 1.23;
$foo = 10.0;
$bar = -INF;
$bar = NAN;

النوع array: المصفوفات

تشبه المصفوفة لائحة قيم، وأبسط صيغة لها هي المصفوفة المُفهرسة (indexed) بأعداد صحيحة ومرتبة حسب الفهرس مع ارتباط العنصر الأول بالفهرس 0.

// مصفوفة من الأعداد الصحيحة
$foo = array(1, 2, 3);

// وما بعدها PHP5.4 صياغة مختصرة للمصفوفة في
$bar = ["A", true, 123 => 5]; 

echo $bar[0]; 
// "A" 

echo $bar[1]; 
// true 

echo $bar[123]; 
// 5 

echo $bar[1234]; 
// null 

يمكن أيضًا أن يكون المفتاح مختلفًا عن العدد الصحيح، في PHP كل المصفوفات خلف الكواليس هي مصفوفات ترابطية (associative) لكن عندما نذكر "مصفوفة ترابطية" (associative arrays) بشكلٍ صريح فإننا نعني أنّ أغلب المفاتيح ليست أعدادًا صحيحةً.

ملاحظة: خصصت بعض لغات البرمجة أنواعًا خاصة بالمصفوفة الترابطية مثل Object أو Dictionary أو Map.

$array = array();
$array["foo"] = "bar";
$array["baz"] = "quux";
$array[42] = "hello";

echo $array["foo"]; 
// "bar" الخرج

echo $array["bar"]; 
// "quux" الخرج

echo $array[42]; 
// "hello" الخرج

النوع string: السلاسل النصية

تشبه السلسلة النصية مصفوفة محارف.

$foo = "bar";

يمكن فهرسة السلسلة النصية لتُرجع محارفها الإفرادية تمامًا مثل المصفوفة:

$foo = "bar";

echo $foo[0]; 
// الحرف الأول من السلسلة النصية 'b'‎ يطبع

النوع object: الكائنات

الكائن هو نسخة من الصنف، يمكن الوصول إلى متغيراته وتوابعه بالعامل ‎->‎.

// المعرّف مسبقًا صنف فارغ stdClass إنشاء كائن جديد من الصنف
$foo = new stdClass(); 
$foo->bar = "baz";

echo $foo->bar; 
// "baz" الخرج

// أو يمكننا تحويل المصفوفة إلى كائن
$quux = (object) ["foo" => "bar"];

echo $quux->foo; // "bar" الخرج

النوع resources: الموارد

تحمل الموارد مقابض (handles) لفتح الملفات، اتصالات قاعدة البيانات، مجاري الدخل والخرج، مناطق الصورة وما يشبه ذلك (كما ذُكر في المرجع).

//الدالة التي تفتح ملفًا على القرص على أنّه مورد fopen()
$fp = fopen('file.ext', 'r'); 

var_dump($fp); 
// resource(2) of type (stream) 

نستخدم الدالة gettype()‎ للحصول على نوع المتغير على شكل سلسلة نصية.

echo gettype(1); 
// "integer" الخرج

echo gettype(true);
 // "boolean"

التلميح عن النوع

ميزة التلميح عن نوع الأصناف والواجهات

أُضيفت ميزة التلميح عن نوع (Type hinting) الأصناف والواجهات في الإصدار PHP 5.

التلميح عن نوع الصنف

<?php

class Student
{
    public $name = 'Chris';
}

class School
{
    public $name = 'University of Edinburgh';
}

function enroll(Student $student, School $school)
{
    echo $student->name . ' is being enrolled at ' . $school->name;
}

$student = new Student();
$school = new School();
enroll($student, $school);

خرج السكربت السابق:

Chris is being enrolled at University of Edinburgh

التلميح عن نوع الواجهة

<?php
interface Enrollable {};
interface Attendable {};

class Chris implements Enrollable
{
    public $name = 'Chris';
}

class UniversityOfEdinburgh implements Attendable
{
    public $name = 'University of Edinburgh';
}

function enroll(Enrollable $enrollee, Attendable $premises)
{
    echo $enrollee->name . ' is being enrolled at ' . $premises->name;
}

$chris = new Chris();
$edinburgh = new UniversityOfEdinburgh();
enroll($chris, $edinburgh);

خرج الشيفرة السابقة هو نفس خرج المثال الأول:

Chris is being enrolled at University of Edinburgh

التلميح باستخدام الكلمة المفتاحية self

يمكن استخدام الكلمة المفتاحية self كتلميح عن النوع للإشارة إلى أنّ القيمة يجب أن تكون نسخة من الصنف الذي يصرّح عن التابع.

التلميح عن النوع للأنواع العددية والمصفوفات والأنواع القابلة للاستدعاء

أُضيفَ دعم التلميح عن نوع معاملات المصفوفة في الإصدار PHP 5.1 (والقيم المعادة بعد الإصدار PHP 7.1) باستخدام الكلمة المفتاحية array، تعدّ أيّة مصفوفة مهما كان نوعها وأبعادها وحتى المصفوفات الصفرية قيمةً صحيحةً.

أُضيف دعم التلميح عن النوع القابل للاستدعاء (callable) في الإصدار PHP 5.4، وتعدّ أي قيمة قابلة للاستدعاء صحيحةً للمعاملات والقيم المعادة الملمّح عنها أنّها قابلة للاستدعاء، مثل كائنات المُغلِّف والسلاسل النصية التي تعبّر عن اسم الدالة والمصفوفة ‎array(class_name|object,method_name)‎.

إذا حدث خطأ كتابي في اسم الدالة وأصبحت غير قابلة للاستدعاء تظهر رسالة خطأ أقل وضوحًا:

Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be of the type callable, string/array given
function foo(callable $c) {}

// شيفرة صحيحة
foo("count");

// شيفرة صحيحة
foo("Phar::running");

// شيفرة صحيحة
foo(["Phar", "running");

// شيفرة صحيحة
foo([new ReflectionClass("stdClass"), "getName"]);

// شيفرة صحيحة
foo(function() {});

foo("no_such_function"); 
// callable expected, string given

يمكن أيضًا تمرير التوابع غير الساكنة على أنّها قابلة للاستدعاء بصيغة ثابتة مما يؤدي إلى ظهور تحذير مُهمَل وخطأ من المستوى E_STRICT في كل من PHP 7 وPHP 5 على الترتيب.

يجب أن ننتبه أيضًا إلى مرئية التابع، إذا كان سياق التابع مع المعامل القابل للاستدعاء لا يمكنهما الوصول إلى المعامل القابل للاستدعاء سينتهي الأمر كما لو أنّ التابع غير موجود.

class Foo{
    private static function f(){
        echo "Good" . PHP_EOL;
    }

    public static function r(callable $c){
        $c();
    }
}

function r(callable $c){}

Foo::r(["Foo", "f"]);

r(["Foo", "f"]);

الخرج:

Fatal error: Uncaught TypeError: Argument 1 passed to r() must be callable, array given

أُضيف دعم التلميح عن الأنواع العددية في PHP 7، أي حصلنا على دعم التلميح عن booleans (القيم المنطقية) وintegers (الأعداد الصحيحة) وfloats (الأعداد العشرية) وstrings (السلاسل النصية).

<?php
function add(int $a, int $b) {
    return $a + $b;
}

var_dump(add(1, 2)); 
// "int(3)"

تحاول PHP بشكلٍ افتراضي تحويل أي وسيط مُعطى ليطابق تلميحه عن النوع، إذا غيّرنا الاستدعاء للشيفرة add(1.5, 2)‎ سنحصل على نفس الخرج تمامًا لأن PHP تحول العدد العشري 1.5 إلى عدد صحيح، لنوقف هذا السلوك نضيف الشيفرة التالية declare(strict_types=1);‎ في بداية كل ملف PHP مصدري يتطلب ذلك، مثال:

<?php
declare(strict_types=1);

function add(int $a, int $b) {
    return $a + $b;
}

var_dump(add(1.5, 2));

تنتج الشيفرة السابقة خطأً فادحًا:

Fatal error: Uncaught TypeError: Argument 1 passed to add() must be of the type integer, float given

استثناء: أنواع خاصة

قد تعيد بعض دوال PHP قيمة من النوع resource، وبما أنّ هذه ليست قيمة عددية إنّما نوع خاص فمن غير الممكن التلميح عن نوعها، فمثلًا تعيد كل من الدالتين curl_init()‎ وfopen()‎ موردًا وهذين الموردين غير متوافقين بالطبع لذا ترمي PHP 7 خطأ من الصنف TypeError عند كتابة تلميح عن النوع مورد بشكلٍ صريح:

TypeError: Argument 1 passed to sample() must be an instance of resource, resource given

التلميح عن النوع Nullable

المعاملات

أُضيف التلميح عن النوع Nullable في الإصدار PHP 7.1 باستخدام العامل ? قبل التلميح عن النوع.

function f(?string $a) {}
function g(string $a) {}

// شيفرة صحيحة
f(null); 

g(null); 
// TypeError: Argument 1 passed to g() must be of the type string, null given

قبل الإصدار PHP 7.1 إذا كان للمعامل تصريح عن النوع يجب أن يصرّح عن قيمة افتراضية null لقبول هذه القيمة.

function f(string $a = null) {}
function g(string $a) {}

// شيفرة صحيحة
f(null);

g(null); 
// TypeError: Argument 1 passed to g() must be of the type string, null given

القيم المعادة

في الإصدار PHP 7.0 لا يمكن للدوال مع قيمة معادة أن تعيد قيمة فارغة null، وبدءًا من الإصدار PHP 7.1 يمكن للدوال التصريح عن تلميح نوع القيمة المعادة أنّها nullable وعندها يجب أن تعيد الدالة قيمة فارغة null وليس void (أي من غير الممكن عدم كتابة تعليمة return أو كتابتها فارغة).

function f() : ?string {
    return null;
}

function g() : ?string {}
function h() : ?string {}

// شيفرة صحيحة
f(); 

g(); 
// TypeError: Return value of g() must be of the type string or null, none returned

h(); 
// TypeError: Return value of h() must be of the type string or null, none returned

التصريح عن نوع الكائنات العامة

بما أنّ كائنات PHP لا ترث من أي صنف أساسي (بما في ذلك الصنف stdClass) فلا يوجد دعم للتصريح عن نوع الكائنات العامة، مثلًا الشيفرة التالية غير صحيحة:

<?php
function doSomething(object $obj) {
    return $obj;
}

class ClassOne {}
class ClassTwo {}

$classOne= new ClassOne();
$classTwo= new ClassTwo();

doSomething($classOne);
doSomething($classTwo);

وسترمي الخطأ الفادح:

Fatal error: Uncaught TypeError: Argument 1 passed to doSomething() must be an instance of object, instance of OperationOne given

الحل البديل لذلك هو التصريح عن واجهة لا تعرّف أي توابع ثم نجعل كل الكائنات تنفّذ هذه الواجهة.

<?php
interface Object {}

function doSomething(Object $obj) {
    return $obj;
}

class ClassOne implements Object {}
class ClassTwo implements Object {}

$classOne = new ClassOne();
$classTwo = new ClassTwo();

doSomething($classOne);
doSomething($classTwo);

التصريح عن نوع دون قيمة معادة (Void)

أُضيف نوع القيمة المعادة void في الإصدار PHP 7.1، على الرغم من أنّ PHP ليس فيها قيمة void فعلية إلا أنّه من المعروف عمومًا في عالم البرمجة أنّ الدالة التي لا تعيد شيئًا فإنّ القيمة المعادة هي void. يجب ألّا نخلط بين القيمة المعادة void والقيمة المعادة الفارغة null إذ أنّ القيمة null هي قيمة يمكن إعادتها.

function lacks_return(): void {
    // شيفرة صحيحة
}

لاحظ أنّه إذا صرّحت عن القيمة المعادة أنّها void لا يمكنك أن تعيد أي قيم وإلا ستحصل على خطأ فادح:

function should_return_nothing(): void {
    return null; 
}

// Fatal error: A void function must not return a value

إلا أنّه يمكن استخدام return للخروج من الدالة:

function returns_nothing(): void {
    // شيفرة صحيحة
    return; 
}

تحويل الأنواع ومشاكل الموازنة غير الصارمة

ما هو تحويل النوع (Type Juggling)؟

تعدّ PHP لغة متهاونة في تحديد النوع (loosely typed) أي أنّها لا تتطلب أن يكون المعامَلات (operands) في تعبير ما من نفس النوع (أو من أنواع متوافقة)، فمثلًا يمكنك أن تتوقع أنّ إضافة رقم إلى سلسلة نصية ستعمل بشكلٍ صحيح.

var_dump ("This is example number " . 1);

الخرج:

string(24) "This is example number 1"

تحقق PHP هذا عن طريق التحويل بين أنواع المتغيرات غير المتوافقة بشكلٍ تلقائي مما يسمح بإجراء العملية المطلوبة، ستحوّل PHP في مثالنا السابق العدد 1 إلى سلسلة نصية وتصبح قابلة للدمج مع السلسلة النصية التي تسبقها، هذا ما يسمى التحويل بين الأنواع (type juggling) وهذه ميزة قوية جدًا في PHP لكنها في نفس الوقت يمكن أن تؤدي إلى الكثير من الحيرة إذا لم تنتبه لها ومن الممكن أن تؤدي إلى مشاكل في الحماية أيضًا.

بفرض لدينا الشيفرة التالية:

if (1 == $variable) {
    // القيام بشيء ما
}

يبدو أنّ المبرمج يقصد من الشيفرة السابقة التأكّد من أنّ قيمة المتغير هي 1،لكن ما الذي يحدث إذا كانت قيمته "1‎ and a half"، قد يفاجئك الجواب:

$variable = "1 and a half";
var_dump (1 == $variable);

النتيجة هي:

bool(true)

يحدث هذا لأنّ PHP تدرك أنّ السلسلة النصية "1‎ and a half" ليست عددًا صحيحًا، لكنها تحتاج للموازنة مع العدد الصحيح 1، فتقوم بمحاولة تحويل المتغير إلى عدد صحيح، وذلك بأخذ كل المحارف في بداية السلسلة النصية التي يمكن تحويلها إلى عدد صحيح وتحوّلها ثم تتوقف عندما تواجه محرفًا لا يمكنها التعامل معه كرقم، لذا ستُحوَّل "1‎ and a half" إلى 1.

يوضّح لنا المثال السابق هذه القضية، ستغطي الأمثلة القليلة التالية بعض الحالات التي قد يؤدي تحويل النوع فيها إلى أخطاء في التطبيقات الحقيقية.

القراءة من ملف

نحتاج عند القراءة من ملف أن نكون قادرين على معرفة متى نصل إلى نهايته، يمكننا استخدام الدالة fgets()‎ كشرط في حلقة إذ أنّها تُرجع القيمة false عند نهاية الملف، لكن ذلك قد يسبب قطع الحلقة في وقت سابق لأوانه إذا كانت البيانات المُرجعَة من القراءة الأخيرة تُقيَّم على أنّها القيمة المنطقية false.

$handle = fopen ("/path/to/my/file", "r");

if ($handle === false) {
    throw new Exception ("Failed to open file for reading");
}

while ($data = fgets($handle)) {
    echo ("Current file line is $data\n");
}

fclose ($handle);

إذا كان الملف المقروء يحتوي سطرًا فارغًا فإنّ حلقة while ستنتهي عند تلك النقطة لأنَّ السلسلة النصية الفارغة تُقيَّم على أنّها القيمة المنطقية false.

يمكننا بدلًا من ذلك التحقق من القيمة المنطقية false بشكلٍ صريح باستخدام عامل المساواة الصارمة:

while (($data = fgets($handle)) !== false) {
    echo ("Current file line is $data\n");
}

لاحظ أنّ هذا المثال نظري أما في التطبيقات العملية نستخدم الحلقة التالية:

while (!feof($handle)) {
    $data = fgets($handle);
    echo ("Current file line is $data\n");
}

أو استبدال كل ذلك بالشيفرة:

$filedata = file("/path/to/my/file");
foreach ($filedata as $data) {
    echo ("Current file line is $data\n");
}

مفاجآت Switch

تستخدم تعليمات switch الموازنة غير الصارمة لتحديد التطابقات، يمكن أن يؤدي هذا إلى بعض المفاجآت السيئة، ليكن لدينا الشيفرة التالية مثلًا:

switch ($name) {
    case 'input 1':
        $mode = 'output_1';
        break;

    case 'input 2':
        $mode = 'output_2';
        break;

    default:
        $mode = 'unknown';
        break;
}

الشيفرة السابقة بسيطة جدًا وتعمل كما هو متوقع عندما يكون المتغير ‎$name سلسلة نصية وإلا ستسبب بعض المشاكل، فمثلًا إذا كان المتغير عددًا صحيحًا 0 سيتحول النوع عند الموازنة، لكن يتم التعامل مع القيمة الحرفية المُحوّلة في تعليمة case وليس في شرط تعليمة switch، تُحوَّل السلسلة النصية "input 1" إلى العدد الصحيح 0 والذي يُطابق القيمة المدخلة للعدد الصحيح 0. النتيجة النهائية إذا كانت قيمة العدد الصحيح 0 هي تنفيذ الحالة الأولى دائمًا.

يوجد بعض الحلول لهذه المشكلة: التحويل الصريح بين الأنواع يمكن تحويل نوع القيمة إلى سلسلة نصية قبل الموازنة:

switch ((string)$name) {
    ...
}

أو يمكننا استخدام دالة تُرجع سلسلة نصية كما في الشيفرة التالية:

switch (strval($name)) {
    ...
}

تضمن كِلا الطريقتين أن تكون القيمة من نفس نوع القيمة في تعليمات case.

تجنب switch

يوفر لنا استخدام تعليمة if التحكم في كيفية حدوث الموازنة مما يسمح لنا باستخدام عوامل الموازنة الصارمة:

if ($name === "input 1") {
    $mode = "output_1";
} elseif ($name === "input 2") {
    $mode = "output_2";
} else {
    $mode = "unknown";
}

الكتابة الصارمة (Strict typing)

يمكن التخفيف من بعض الآثار الضارة لتحويل النوع بدءًا من الإصدار PHP 7.0 باستخدام الكتابة الصارمة، تفرض PHP التصريح عن نوع المعامل والقيمة المعادة برمي استثناء TypeError من خلال تضمين تعليمة declare في السطر الأول من الملف.

declare(strict_types=1);

فمثلًا في الشيفرة التالية استخدام تعريفات نوع المعامل سيرمي استثناءً قابلًا للالتقاط من النوع TypeError عند التنفيذ:

<?php
declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}
echo sum("1", 2);

وبالتالي تستخدم هذه الشيفرة التصريح عن نوع القيمة المُعادة وسترمي استثناءً إذا كانت القيمة من أي نوع غير العدد الصحيح:

<?php
declare(strict_types=1);

function returner($a): int {
    return $a;
}

returner("this is a string");

أفضل ممارسات المتغيرات العامة (Global variable)

سنوضح مشكلةً مع هذه الشيفرة الزائفة:

function foo() {
    global $bob;
    $bob->doSomething();
}

سؤالك الأول هنا بالطبع هو: مين أين أتى المتغير ‎$bob؟

هل شعرت بالحيرة؟ إذن قد عرفت لماذا تسبب المتغيرات العامة حيرةً وتعد ممارسةً سيئةً.

إذا كان هذا برنامجًا حقيقيًا فستكون خطوتك التالية تتبع كل المتغيرات ‎$bob وآمل أن تجد المتغير الصحيح (من السيء أن يكون اُستعمل بشكلٍ متكرر)، والأسوأ أن يكون شخصًا ما قد عرّفه (أو نسيت وأعدت استخدام هذا المتغير) وعندها ستتوقف شيفرتك، ففي مثال الشيفرة السابق، سيسبب وجود كائن خاطئ أو عدم وجود كائن على الإطلاق خطأً فادحًا (fatal error).

جميع برامج PHP الفعلية تستخدم شيفرة مثل include('file.php');‎ لذا كلما ضُمّنت المزيد من الملفات فإنّ مهمتك في إصلاح الشيفرة تصحيح أصعب، وهذا أيضًا يجعل مهمة اختبار التطبيق أصعب، افترض أنّك استخدمت متغيرًا عامًا ليحمل اتصالك بقاعدة البيانات:

$dbConnector = new DBConnector(...);

function doSomething() {
    global $dbConnector;
    $dbConnector->execute("...");
}

يجب تجاوز المتغير العام ‎$dbConnector‎ لكتابة وحدة اختبار لهذه الدالة ونفّذ الاختبار ثمّ أعد تعيينه بقيمته الأصلية وهذا شيء معرّض للخطأ بشكلٍ كبير:

/**
* @اختبار
*/
function testSomething() {
    global $dbConnector;

    // قُم بنسخ احتياطي
    $bkp = $dbConnector; 

    // تجاوز
    $dbConnector = Mock::create('DBConnector');

    assertTrue(foo());

    // أعد التخزين
    $dbConnector = $bkp; 
}

كيف نتجنب المتغيرات العامة؟

تُدعى أفضل طريقة لتجنب المتغيرات العامة فلسفيًا "إضافة الاعتماديّات" (Dependency Injection)، تسمح لنا هذه الطريقة بإضافة الأدوات التي نحتاجها داخل الدالة أو الصنف.

function foo(\Bar $bob) {
    $bob->doSomething();
}

هذه الطريقة أسهل للفهم والإصلاح، إذ ليس هناك تخمين أين أعُدَّ المتغير ‎$bob لأنّ المستدعي مسؤول عن معرفة ذلك (يمرر لنا ما نحتاج معرفته). والأفضل من ذلك أنّه يمكننا استخدام تصريحات النوع لتقييد ما يتم تمريره.

لذا نعلم أنّ المتغير ‎$bob إما نسخةً من الصنف Bar أو من الصنف الابن له وهذا يعني أنّه يمكننا استخدام توابع هذا الصنف، نستطيع الآن بالاشتراك مع محمّل آلي قياسي (متوفر من الإصدار PHP 5.3) تتبّع أين عُرِّف Bar. يشتمل الإصدار PHP 7.0 وما بعده على تصريحات نوع موسّعة حيث يمكنك أن تستعمل الأنواع العددية (مثل int أو string).

المتغيرات ذات النطاق العام العالي (Superglobal variables)

المتغيرات ذات النطاق العام العالي في PHP هي متغيرات معرّفة مسبقًا ومتوفرة دائمًا ويمكن الوصول إليها من أي نطاق في أي مكان من الملف البرمجي (ضمن الدوال أو الأصناف أو الملفات) دون الحاجة لأن تعرّفها بالكلمة المفتاحية global.

المتغيرات ذات النطاق العام العالي في PHP هي:

القيم الافتراضية للمتغيرات غير المُهيَّأة (uninitialized)

تهيئة المتغيرات في PHP ليس أمرًا ضروريًا لكنه يعدّ ممارسةً جيّدةً، للمتغيرات غير المُهيَّأة قيمةً افتراضيةً لنوعها وفقًا للسياق الذي اُستخدموا به.

لنرى ما هي قيمة متغيِّر لم تُسنَد له قيمة وليس مرجعًا:

var_dump($unset_var); 
// ‏NULL الخرج

سنهيئ المتغير بالأنواع التالية:

boolean: القيم المنطقية

echo($unset_bool ? "true\n" : "false\n");
 // 'false' الخرج

string: السلاسل النصية

$unset_str .= 'abc';
var_dump($unset_str); 
// 'string(3) "abc"' الخرج

integer: الأعداد الصحيحة

$unset_int += 25; // 0 + 25 => 25
var_dump($unset_int); 
// 'int(25)' الخرج

Float/double: الأعداد العشرية

$unset_float += 1.25;
var_dump($unset_float);
// 'float(1.25)' 

array: المصفوفات

$unset_arr[3] = "def";
var_dump($unset_arr); 
// array(1) { [3]=> string(3) "def" }

object: الكائنات

$unset_obj->foo = 'bar';
var_dump($unset_obj); 
// object(stdClass)#1 (1) { ["foo"]=> string(3) "bar" } 

يعدّ الاعتماد على القيمة الافتراضية لمتغير غير مُهيّأ مشكلةً إذا ضُمِّن ملف داخل ملف آخر يستعمل نفس اسم المتغير.

توكيد قيمة المتغير وعامل التطابق

تملك قيمة المتغير في PHP "توكيدًا" مرتبطًا حتى أنّ القيم غير المنطقية ستساوي true أو false، وهذا سيجعل من الممكن استخدام أي متغيّر في كتلة الشيفرة الشرطية، مثل:

if ($var == true) { /* مثال واضح */ }
if ($var) { /* ضمنيًأ $var == true */ }

بعض القواعد الأساسية لأنواع مختلفة من قيم المتغير:

  • السلاسل النصية ذات الطول غير الصفري تساوي true متضمنةً السلاسل التي تحوي فراغًا فقط مثل ' '.
  • السلاسل الفارغة '' تساوي false.
$var = '';
$var_is_true = ($var == true); 
// false
$var_is_false = ($var == false); 
// true

$var = ' ';
$var_is_true = ($var == true); 
// true
$var_is_false = ($var == false); 
// false
  • الأعداد الصحيحة تساوي true إذا كانت غير صفرية، أما الصفر يساوي false.
$var = -1;
$var_is_true = ($var == true);
// true

$var = 99;
$var_is_true = ($var == true); 
// true

$var = 0;
$var_is_true = ($var == true); 
// false
  • القيمة null تساوي false.
$var = null;
$var_is_true = ($var == true); 
// false
$var_is_false = ($var == false); 
// true
  • السلاسل الفارغة '' والسلسلة الصفرية '0' تساوي false.
$var = '';
$var_is_true = ($var == true); 
// false
$var_is_false = ($var == false); 
// true

$var = '0';
$var_is_true = ($var == true); 
// false
$var_is_false = ($var == false); 
// true
  • الأعداد العشرية تساوي true إذا كانت غير صفرية وتساوي false إذا كانت صفرية.
    • القيمة NAN (‏Not-A-Number) تساوي true، أي نتيجة NAN == true هي true لأنّ NAN هي قيمة عشرية غير صفرية.
    • القيم الصفرية تتضمن 0- و0+ كما عرّفها معيار IEEE 754. لا تميّز PHP بين 0- و0+ في الأعداد العشرية ذات الدقة المضاعفة أي نتيجة floatval('0') == floatval('-0')‎ هي true.
      • إنّ floatval('0') === floatval('-0')‎.
      • بالإضافة إلى ذلك floatval('0') == false وfloatval('-0') == false.
$var = NAN;
$var_is_true = ($var == true); 
// true
$var_is_false = ($var == false); 
// false

$var = floatval('-0');
$var_is_true = ($var == true); 
// false
$var_is_false = ($var == false); 
// true

$var = floatval('0') == floatval('-0');
$var_is_true = ($var == true); 
// false
$var_is_false = ($var == false); 
// true

عامل التطابق

يوجد عامل التطابق === في التوثيق الرسمي لعوامل الموازنة في لغة PHP، يمكن أن يُستخدم هذا العامل للتحقق إذا كان المتغير مطابقًا لقيمة مرجعية.

$var = null;
$var_is_null = $var === null; 
// true
$var_is_true = $var === true; 
// false
$var_is_false = $var === false; 
// false

ويوجد عامل مقابل له وهو عامل عدم التطابق ==!:

$var = null;
$var_is_null = $var !== null; 
// false
$var_is_true = $var !== true; 
// true
$var_is_false = $var !== false; 
// true

يمكن أن يُستخدم عامل التطابق بدلًا عن بعض دوال اللغة مثل دالة is_null()‎.

حالة استخدام مع دالة strpos()‎

تحدد الدالة strpos($haystack, $needle)‎ موقع أول ظهور لمحارف المعامل ‎$needle‎ ضمن السلسلة النصية ‎$haystack أو عدم ظهوره أبدًا في السلسلة وهي دالة حساسة لحالة الأحرف، يمكنك استخدام الدالة stripos($haystack, $needle)‎ إذا أردت دالةً تؤدي نفس العمل ولكن غير حساسة لحالة الأحرف.

تتضمن الدالتان strpos وstripos معاملًا ثالثًا offset (int)‎ وهو اختياري يحدد إزاحة المؤشر قبل أن يبدأ البحث وعلى عكس دالتي strrpos وstrripos لا يمكن أن يكون هذا العدد سالبًا.

يمكن أن تُرجع الدالة:

  • 0 إذا وُجِد ‎$needle‎ في بداية السلسلة النصية ‎$haystack.
  • عددًا صحيحًا غير صفريًا يحدد موقع وجود ‎$needle‎ إذا وُجد في مكان غير بداية السلسلة النصية ‎$haystack.
  • القيمة false إذا لم يوجد ‎$n‎eedle في السلسلة النصية ‎$haystack.

إنّ كلّ من 0 وfalse لهما توكيد false في PHP ولكنهما يمثلان حالات مختلفة بالنسبة لدالة strpos لذا من المهم أن نميز بينهما باستخدام عامل التطابق === لنميز بين القيمة false والقيمة 0 التي تساوي false.

$idx = substr($haystack, $needle);
if ($idx === false)
{
    // $haystack ضمن $needle‎‎ ‏‎‏منطقية عندما لا يوجد المعامل for تعليمة 
}
else
{
    // $haystack ضمن $needle‎‎ ‏‎‏منطقية عندما يوجد المعامل for تعليمة  
}

يمكن استخدام عامل عدم التطابق بدلًا من ذلك:

$idx = substr($haystack, $needle);
if ($idx !== false)
{
    // $haystack ضمن $needle‎‎ ‏‎‏منطقية عندما يوجد المعامل for تعليمة  
}
else
{
    // $haystack ضمن $needle‎‎ ‏‎‏منطقية عندما لا يوجد المعامل for تعليمة 
}

نطاق المتغيرات

يشير نطاق المتغير (Variable scope) إلى مناطق الشيفرة التي يمكن منها الوصول للمتغير، وهذا ما يُسمّى أيضًا بالمرئية (visibility)، يُعرّف نطاق كتل الشيفرة في PHP بالدوال، الأصناف، ونطاق عام متاح عبر التطبيق.

المتغيرات ذات النطاق العام العالي (Superglobal variables)

إنّ المتغيرات ذات النطاق العام العالي (Superglobal variables) معرّفة في PHP ويمكن استخدامها في أي مكان بدون الكلمة المفتاحية global.

<?php
function getPostValue($key, $default = NULL) {
    // ‎متغير ذو نطاق عام عالي‎‌ $_POST
    // ‎'global $_POST;'‎ لذا يمكن استخدامه دون ذكر
    if (isset($_POST[$key])) {
        return $_POST[$key];
    }
    return $default;
}

// $_POST['username'] يسترجع
echo getPostValue('username');

// $default ويسند سلسلة فارغة للمتغير $_POST['email'] يسترجع
echo getPostValue('email', '');

الخاصيّات الساكنة والمتغيرات

تعدّ خاصيّات الصنف الساكنة المعرّفة مع المرئية public وظيفيًا نفس المتغيرات العامة، يمكن الوصول إليها من أيّ مكان في الصنف الذي عُرّفت ضمنه.

class SomeClass {
    public static int $counter = 0;
}

// من أي مكان $counter يمكن قراءة/كتابة المتغير الساكن
// ولا يتطلب ذلك إنشاء نسخة من الصنف 
SomeClass::$counter += 1;

يمكن أن تعرّف الدوال أيضًا متغيرات ساكنة داخل نطاقها الخاص، تبقى هذه المتغيرات ثابتةً عند استدعاءات الدالة المتعددة على عكس المتغيرات العادية المعرّفة ضمن نطاق الدالة، ويمكن أن يكون هذا أسلوبًا سهلًا وبسيطًا جدًا لتحقيق نمط التصميم المتفرّد (Singleton design pattern).

class Singleton {
    public static function getInstance() {

        // عندما تنتهي الدالة $instance لا يُحذف المتغير الساكن
        static $instance;
        // (1)

        if (!$instance) {
            // (2)
            $instance = new Singleton();
        }
        return $instance;
    }

}

$instance1 = Singleton::getInstance();
$instance2 = Singleton::getInstance();

// (3)
var_dump($instance1 === $instance2);

في الموضع (1) لن يدخل الاستدعاء الثاني إلى تعليمة if لأنّ نسخة من الصنف Singleton خُزِّنت في المتغير ‎$instance وتبقى ثابتةً عبر الاستدعاءات المتعددة.

سيصل الاستدعاء الأول لهذه الدالة إلى الموضع (2) لأنّ المتغير ‎$instance صُرِّح عنه ولم يُهيَّئ.

بموازنة الكائنات في الموضع (3) باستخدام العامل '===' الذي يتحقق من تطابق الكائنات ستُطبع true لأنّ المتغير الساكن ‎$instance يبقى ثابتًا عبر الاستدعاءات المتعددة للتابع getInstance()‎.

المتغيرات العامة التي يعرّفها المستخدم

النطاق العام هو النطاق الموجود خارج أيّ دالة أو صنف، ويبقى النطاق نفسه عندما يتضمن النص البرمجي المكتوب بلغة PHP نصًا برمجيًا آخر (باستخدام include أو require)، إذا ضُمِّن الملف البرمجي خارج الدوال والأصناف فإنّ متغيراته العامة تكون أيضًا بالنطاق العام للملف المُضمِّن أمّا إذا ضُمِّن داخل دالة فإنّ متغيراته تبقى ضمن نطاق الدالة.

يمكن استخدام الكلمة المفتاحية global لينشئ المستخدم متغيرات عامة ضمن نطاق الدالة أو تابع الصنف.

<?php
    $amount_of_log_calls = 0;
    function log_message($message) {
        // الوصول للمتغير العام من نطاق الدالة يتطلب هذه التعليمة الصريحة
        global $amount_of_log_calls;

        // هذا التعديل على المتغير العام ثابت
        $amount_of_log_calls += 1;
        echo $message;
    }

// عندما تكون في النطاق العام يمكن استخدام المتغيرات العامة العادية
// 'global $variable;' بدون التعليمة الصريحة 
echo $amount_of_log_calls; // 0

log_message("First log message!");
echo $amount_of_log_calls; // 1

log_message("Second log message!");
echo $amount_of_log_calls; // 2

الطريقة الثانية للوصول للمتغيرات من النطاق العام هي باستخدام مصفوفة PHP المعرّفة ‎$GLOBALS، وهي مصفوفة ترابطية (associative) فيها المفتاح هو اسم المتغير العام وقيمة عنصر المصفوفة هي محتوى المتغير وهي مصفوفة ذات نطاق عام عالي لذا يمكن الوصول إليها من أي نطاق. هذا يعني أنه يمكن إعادة كتابة الدالة log_message()‎ بالشكل التالي:

function log_message($message) {
    // $amount_of_log_calls الوصول إلى المتغير العام
    // 'global $GLOBALS;' لا يحتاج إلى التصريح $GLOBALS باستخدام المصفوفة 
    // لأنها ذات نطاق عام عالي
    $GLOBALS['amount_of_log_calls'] += 1;
    echo $messsage;

قد تتساءل لماذا نستخدم المصفوفة ‎ $GLOBALS‎‎بينما يمكننا استخدام الكلمة المفتاحية global للحصول على قيمة المتغير العام؟ السبب الرئيسي هو أنّ استخدام الكلمة المفتاحية global ستجلب المتغير إلى النطاق وعندها لا يمكنك إعادة استخدام نفس اسم المتغير في النطاق المحلي (local scope).

تعريف الثوابت

تُنشأ الثوابت بالتعليمة const مع الدالة define، ومن الشائع استخدام الأحرف الكبيرة في تسميتها.

تعريف ثابت باستخدام القيم الصريحة

// عدد عشري
const PI = 3.14; 

// قيمة منطقية
define("EARTH_IS_FLAT", false); 

// null
const "UNKNOWN" = null; 

// سلسلة نصية
define("APP_ENV", "dev"); 

// عدد صحيح باستخدام تعبير عددي
const MAX_SESSION_TIME = 60 * 60;

// مصفوفة
const APP_LANGUAGES = ["de", "en"]; 
define("BETTER_APP_LANGUAGES", ["lu", "de"]); 

تعريف ثابت باستخدام ثابت آخر

يمكنك الاعتماد على ثابت موجود لديك لتسمية ثابت جديد:

const TAU = PI * 2;
define("EARTH_IS_ROUND", !EARTH_IS_FLAT);
define("MORE_UNKNOWN", UNKNOWN);

// يمكن أيضًا استخدام توابع تعديل السلاسل النصية 
// const المثال التالي (استدعاء دالة) لا يعمل مع
define("APP_ENV_UPPERCASE", strtoupper(APP_ENV));

// لأن الوقت ليس تعبير عددي ثابت (fatal error) التعليمة التالية ترمي خطأً فادحًا
// const TIME = time();
define("MAX_SESSION_TIME_IN_MINUTES", MAX_SESSION_TIME / 60);

// تعديل المصفوفات
const APP_FUTURE_LANGUAGES = [-1 => "es"] + APP_LANGUAGES;
define("APP_BETTER_FUTURE_LANGUAGES", array_merge(["fr"], APP_BETTER_LANGUAGES));

الثوابت المحجوزة

يوجد بعض أسماء الثوابت المحجوزة في PHP والتي لا يمكن إعادة تعريفها، إليك بعض الأمثلة:

// ثابت محلي
define("true", false);

// ثابت محلي
define("false", true);

// محمّلة curl سيحدث خطأ إذا كانت الإضافة
define("CURLOPT_AUTOREFERER", "something");

وسيظهر لك الخطأ Constant ... already defined in ...‎

تعريف الثوابت الشرطي

إذا كان لديك عدة ملفات تعرّف فيها نفس المتغير (مثلًا في ملف الإعدادات الرئيسي وملف الإعدادات المحلي) عندها فإنّ الصيغة التالية ستساعدك لتجنّب التضارب:

// إذا لم يُعرَّف من قبل PI عرّف الثابت
defined("PI") || define("PI", 3.1415);

const مقابل define

define تعبير يُستدعى أثناء التنفيذ (runtime expression) أما const يُستدعى عند التفسير، لذا يسمح define بالقيم الديناميكية (مثل استدعاءات الدالة، المتغيرات…) وبالأسماء الديناميكية والتعريف الشرطي، لكن يُعرَّف عادةً نسبةً إلى فضاء اسم الجذر. بينما const هو ثابت (كما هو مسموح في العمليات مع الثوابت الأخرى والأعداد أو المصفوفات ومجموعة محدودة منها فقط ما يسمى التعابير العددية الثابتة أي العوامل الحسابية والمنطقية والموازنة والمصفوفة) ولكن يُسبق تلقائيًا بفضاء الاسم الفعال حاليًا. وتدعم const الثوابت الأخرى والأعداد كالقيم ولا توجد عمليات.

ثوابت الصنف

تُعرّف الثوابت داخل الأصناف باستخدام الكلمة المفتاحية const.

class Foo {
    const BAR_TYPE = "bar";

    // self:: مرجع من داخل الصنف باستخدام
    public function myMethod() {
        return self::BAR_TYPE;
    }
}

// <اسم الصنف‎>:: مرجع من خارج الصنف باستخدام‎‎
echo Foo::BAR_TYPE;

يعدّ هذا مفيدًا لتخزين أنواع من العناصر:

<?php
class Logger {
    const LEVEL_INFO = 1;
    const LEVEL_WARNING = 2;
    const LEVEL_ERROR = 3;

    // يمكن أن نسند للثابت قيمةً افتراضيةً
    public function log($message, $level = self::LEVEL_INFO) {
        echo "Message level " . $level . ": " . $message;
    }
}

$logger = new Logger();

// استخدام القيمة الافتراضية
$logger->log("Info"); 

// استخدام المتغير
$logger->log("Warning", $logger::LEVEL_WARNING);

// استخدام الصنف
$logger->log("Error", Logger::LEVEL_ERROR); // using class

التحقق من تعريف الثابت

تحقق بسيط

نستخدم الدالة defined للتحقق من تعريف ثابت ما، لا تهتم هذه الدالة لقيمة الثابت إنما فقط هل الثابت موجود أم لا حتى إذا كانت قيمته null أو false فسترجع true.

<?php
define("GOOD", false);
if (defined("GOOD")) {
    // "GOOD is defined" يطبع السطر التالي
    print "GOOD is defined" ;

    if (GOOD) {
        // false هي GOOD لا يطبع السطر التالي شيئًا بما أنّ
        print "GOOD is true" ;
    }
}

if (!defined("AWESOME")) {
    // لذا يجب تعريفه AWESOME لم يُعرّف الثابت
    define("AWESOME", true);
}

لاحظ أنّ الثوابت تصبح مرئيةً في شيفرتك في السطر التالي مباشرةً لتعريفها:

<?php
if (defined("GOOD")) {
    // لم يُعرّف بعد GOOD لا يطبع السطر التالي شيئًا بما أنّ
    print "GOOD is defined";
}

define("GOOD", false);
if (defined("GOOD")) {
    // "GOOD is defined" يطبع السطر التالي
    print "GOOD is defined";
}

الحصول على كل الثوابت المعرفة

نستخدم الدالة get_defined_constants لمعرفة كل الثوابت المعرّفة حتى تلك المُنشأة من قِبل PHP:

<?php
$constants = get_defined_constants();
var_dump($constants);
// ستظهر لك قائمة كبيرة

يمكنك استدعاء الدالة في بداية ونهاية السكربت (عادةً بعد استدعاء bootstrap) لتحصل على الثوابت المعرّفة في تطبيقك فقط:

<?php
$constants = get_defined_constants();
define("HELLO", "hello");
define("WORLD", "world");
$new_constants = get_defined_constants();
$myconstants = array_diff_assoc($new_constants, $constants);
var_export($myconstants);

/*
الخرج
array (
'HELLO' => 'hello',
'WORLD' => 'world',
)
*/

إنّ هذه الدالة مفيدة أحيانًا للتنقيح.

استخدام الثوابت

تستخدم الثابت ببساطة بواسطة اسمه:

if (EARTH_IS_FLAT) {
    print "Earth is flat";
}

print APP_ENV_UPPERCASE;

ويمكنك استخدام الدالة constant إذا لم تكن تعرف اسم الثابت مسبقًا، إليك الشيفرة التالية المكافئة للشيفرة السابقة:

$const1 = "EARTH_IS_FLAT";
$const2 = "APP_ENV_UPPERCASE";
if (constant($const1)) {
    print "Earth is flat";
}

print constant($const2);

5.5: المصفوفات الثابتة

يمكن أن تُستخدم المصفوفات كثوابت بسيطة أو ثوابت صنف من الإصدار PHP 5.6 وما بعده:

مثال على ثابت صنف

class Answer {
    const C = [2,4];
}
print Answer::C[1] . Answer::C[0]; // 42

مثال على ثابت بسيط

const ANSWER = [2,4];
print ANSWER[1] . ANSWER[0]; // 42

نُقلت هذه الوظيفة إلى الدالة define منذ الإصدار PHP 7.0 من أجل الثوابت البسيطة:

define('VALUES', [2, 3]);
define('MY_ARRAY', [
    1,
    VALUES,
]);

print MY_ARRAY[1][1]; // 3

الثوابت السحرية

الفرق بين FUNCTION وMETHOD

ترجع __FUNCTION__ اسم الدالة فقط بينما تُرجع __METHOD__ اسم الصنف مع اسم الدالة:

<?php
class trick
{
    public function doit()
    {
        echo __FUNCTION__;
    }

    public function doitagain()
    {
        echo __METHOD__;
    }
}

$obj = new trick();
$obj->doit(); // doit
$obj->doitagain(); // trick::doitagain

الفرق بين CLASS وget_class()‎ وget_called_class()‎

يُرجع الثابت السحري __CLASS__ نفس نتيجة استدعاء الدالة get_class()‎ بدون معاملات ويُرجع كلاهما اسم الصنف حيث يُعرّف (أي حيث كتبت استدعاء الدالة/ اسم الثابت).

وفي المقابل فإنّ كلًا من استدعاء الدوال get_class($this)‎ وget_called_class()‎ يُرجع اسم الصنف الفعلي الذي اُستنسخ:

<?php
class Definition_Class {
    public function say(){
        echo '__CLASS__ value: ' . __CLASS__ . "\n";
        echo 'get_called_class() value: ' . get_called_class() . "\n";
        echo 'get_class($this) value: ' . get_class($this) . "\n";
        echo 'get_class() value: ' . get_class() . "\n";
    }
}

class Actual_Class extends Definition_Class {}
$c = new Actual_Class();
$c->say();

// الخرج
// __CLASS__ value: Definition_Class
// get_called_class() value: Actual_Class
// get_class($this) value: Actual_Class
// get_class() value: Definition_Class

ثوابت الملف والمجلد

  • الملف الحالي: يمكنك الحصول على ملف PHP الحالي (مع المسار المطلق) باستخدام الثابت السحري __FILE__، يستخدم هذا غالبًا للتنقيح/ التسجيل (logging):
echo "We are in the file:" , __FILE__ , "\n";
  • المجلد الحالي: يمكنك استخدام الثابت السحري __DIR__ للحصول على المسار المطلق للمجلد الذي يوجد فيه الملف الحالي:
echo "Our script is located in the:" , __DIR__ , "\n";

يمكنك استخدام dirname(__FILE__)‎ للحصول على المسار المطلق للمجلد الذي يوجد فيه الملف الحالي:

echo "Our script is located in the:" , dirname(__FILE__) , "\n";

تُستخدم ثوابت الحصول على المجلد الحالي غالبًا من قِبل أطر عمل PHP لضبط المجلد الأساسي (base directory):

// لإطار العمل index.php
// استخدام الثابت السحري لتعريف ثابت عادي
define(BASEDIR, __DIR__);

// somefile.php looks for views:
// views إذا أردت الوصول لمسار ملف ما في مجلد 
$view = 'page';
$viewFile = BASEDIR . '/views/' . $view;

الفواصل

يفهم نظام ويندوز الخط المائل / في المسارات لذا يُستخدم DIRECTORY_SEPARATOR بشكلٍ أساسي عند تحليل المسارات.

وبالإضافة إلى الثوابت السحرية يوجد في PHP بعض الثوابت الثابتة للعمل مع المسارات مثل الثابت DIRECTORY_SEPARATOR لفصل المجلدات في مسار، يأخد القيمة / في ‎*nix‎ و\ في ويندوز، يمكن كتابة المثال السابق بالشكل التالي:

$view = 'page';
$viewFile = BASEDIR . DIRECTORY_SEPARATOR .'views' . DIRECTORY_SEPARATOR . $view;

نادرًا ما يستخدم الثابت PATH_SEPARATOR لفصل المسارات في متغير البيئة ‎$PATH وقيمته ; في ويندوز و: في غيره.

ترجمة -وبتصرف- للفصول [Variables - Variable Scope - Constants - Magic Constants] من كتاب PHP Notes for Professionals book

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...